package com.yanqu.road.server.channelserver.manager.activity.tonbing.activity;

import com.alibaba.fastjson.JSON;
import com.yanqu.road.entity.activity.ActivityConditionInfo;
import com.yanqu.road.entity.activity.tonbingchannel.ChannelTonBingRankSort;
import com.yanqu.road.entity.activity.tonbingchannel.config.*;
import com.yanqu.road.entity.activity.tonbingchannel.data.*;
import com.yanqu.road.entity.activity.tonbingchannel.join.ChannelTonBingUnionJoin;
import com.yanqu.road.entity.activity.tonbingchannel.join.ChannelTonBingUnionJoinMember;
import com.yanqu.road.entity.log.channel.channeltongbing.LogChannelTonBingCityEvent;
import com.yanqu.road.entity.log.channel.channeltongbing.LogChannelTonBingNpcEvent;
import com.yanqu.road.entity.log.channel.channeltongbing.LogChannelTonBingSoldierChange;
import com.yanqu.road.entity.log.channel.channeltongbing.LogChannelTonBingTeamMove;
import com.yanqu.road.entity.config.goods.GoodsInfo;
import com.yanqu.road.entity.enums.activity.tonbing.*;
import com.yanqu.road.entity.enums.*;
import com.yanqu.road.entity.player.UserBaseInfo;
import com.yanqu.road.entity.scene.ScenePlayerInfo;
import com.yanqu.road.entity.scene.UserScene;
import com.yanqu.road.entity.union.UnionBaseInfo;
import com.yanqu.road.logic.activity.tonbing.logic.TonBingMapLogic;
import com.yanqu.road.logic.bussiness.activity.ChannelTonBingActivityBusiness;
import com.yanqu.road.logic.pb.ChannelTonBingPb;
import com.yanqu.road.logic.pb.PlayerBasePb;
import com.yanqu.road.logic.pb.UnionBasePb;
import com.yanqu.road.pb.activity.ChannelTonBingProto;
import com.yanqu.road.pb.cross.activity.CrossActivityProto;
import com.yanqu.road.pb.worldmessage.WorldMessageProto;
import com.yanqu.road.server.channelserver.logic.ChannelMessageHelper;
import com.yanqu.road.server.channelserver.manager.activity.ChannelActivityInfoMgr;
import com.yanqu.road.server.channelserver.manager.activity.ChannelActivityUnionMgr;
import com.yanqu.road.server.channelserver.manager.activity.tonbing.ChannelTonBingMgr;
import com.yanqu.road.server.channelserver.manager.activity.tonbing.logic.ChannelTonBingChatLogic;
import com.yanqu.road.server.channelserver.manager.activity.tonbing.task.BaseChannelTonBingMatchTask;
import com.yanqu.road.server.channelserver.manager.activity.tonbing.task.impl.*;
import com.yanqu.road.server.channelserver.manager.scene.ChannelSceneMgr;
import com.yanqu.road.server.channelserver.manager.user.ChannelUserMgr;
import com.yanqu.road.server.manager.config.GoodsMgr;
import com.yanqu.road.server.manager.log.ChannelAutoLogMgr;
import com.yanqu.road.server.protocol.ClientProtocol;
import com.yanqu.road.server.protocol.GameErrorCode;
import com.yanqu.road.server.protocol.GameProtocol;
import com.yanqu.road.server.protocol.Protocol;
import com.yanqu.road.utils.RandomHelper;
import com.yanqu.road.utils.date.DateHelper;
import com.yanqu.road.utils.string.StringUtils;
import org.apache.mina.util.ConcurrentHashSet;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

public class ChannelTonBingMatch extends ChannelTonBingBaseMatch {

    private RandomHelper randomHelper = new RandomHelper();

    private List<Long> watchUserIdList = new ArrayList<>();

    /**
     * 活动总数据
     */
    private ChannelTonBingData channelTonBingData;

    /**
     * 商会数据
     */
    private Map<String, ChannelTonBingUnionJoin> unionJoinMap = new ConcurrentHashMap<>();

    /**
     * 玩家数据
     */
    private Map<Long, ChannelTonBingUserData> userDataMap = new ConcurrentHashMap<>();

    /**
     * 阵营数据
     */
    private Map<Integer, ChannelTonBingCampMatchData> campMatchDataMap = new ConcurrentHashMap<>();

    /**
     * 城池战报
     */
    private List<ChannelTonBingCityWarReport> cityWarReportList = new ArrayList();

    /**
     * 个人战报
     */
    private List<ChannelTonBingUserWarReport> userWarReportsList = new ArrayList<>();

    // ============================== 缓存数据 ==============================

    // 待入库战报
    private List<ChannelTonBingUserWarReport> needInsertUserReportList = new ArrayList();
    private List<ChannelTonBingCityWarReport> needInsertCityReportList = new ArrayList();

    // 聊天
    private ChannelTonBingChatLogic chatLogic;

    // 榜单
    private ChannelTonBingRankListModel userRank = new ChannelTonBingRankListModel(); // 个人榜
    private Map<Integer, ChannelTonBingRankListModel> campUserRankMap = new ConcurrentHashMap(); // 阵营内个人榜
    private Map<Integer, ChannelTonBingRankListModel> campUnionRankMap = new ConcurrentHashMap<>(); // 阵营内商会榜

    // 城主职位
    private Map<Long, List<ChannelTonBingUserData>> needSyncPosUserData = new ConcurrentHashMap<>();

    // 需要同步的队伍
    private final Set<String> needSyncTeam = new ConcurrentHashSet<>();

    // 需要同步的城池
    private final Set<Integer> needSyncCity = new ConcurrentHashSet<>();

    // ============================== 构造方法 ==============================
    public void initMatchData(int turn,int warType,int matchId){
        channelTonBingData = new ChannelTonBingData();
        channelTonBingData.setTurn(turn);
        channelTonBingData.setWarType(warType);
        channelTonBingData.setMatchId(matchId);
        channelTonBingData.setActivityId(config.getActivityInfo().getActivityId());
        channelTonBingData.setLockUnion(false);
        channelTonBingData.setProcess2WinUserId(0L);
        channelTonBingData.setRefreshCityEventTime(config.getStage1TimeList().get(turn - 1).get(0)); // 初始化为一阶段开始时间
        channelTonBingData.setApplyCityEventTime(config.getStage1TimeList().get(turn - 1).get(0)); // 初始化为一阶段开始时间
        channelTonBingData.setCityEventMap(createInitCityEvent());
        channelTonBingData.setCampBuffMap(new ConcurrentHashMap<>());
        channelTonBingData.setNpcEventAddTime(config.getStage1TimeList().get(turn - 1).get(0) - config.getNpcEventInterval()); // 这样一阶段开始时才能生成一个
        channelTonBingData.setCityMap(createCity());
        channelTonBingData.setInsertOption();
    }

    public void start(){
        // 【其他初始化】
        chatLogic = new ChannelTonBingChatLogic(config.getActivityInfo());
        // 启动线程
        initTask();
        thread.start();
    }

    public ChannelTonBingMatch(ChannelTonBingConfig tempConfig, ChannelTonBingData data) {
        super(tempConfig);
        if(data != null){
            channelTonBingData = data;
        }
    }

    public void clearChat(){
        if(chatLogic != null){
            chatLogic.clearChat();
        }
    }

    public void addWatchPlayer(long userId){
        if(!watchUserIdList.contains(userId)){
            watchUserIdList.add(userId);
        }
    }

    public void removeWatchPlayer(long userId){
        watchUserIdList.remove(userId);
    }

    // ============================== 数据生成获取 ==============================
    /**
     * 初始化阵营数据
     */
    private void initCampUserData() {

        logger.info("initCampUserData activityId {}.", config.getActivityInfo().getActivityId());

        // 阵营数据生成
        for (int campId = 1; campId <= config.getCampNum(); campId++) {
            if (!campMatchDataMap.containsKey(campId)) {
                ChannelTonBingCampMatchData campData = new ChannelTonBingCampMatchData();
                campData.setActivityId(config.getActivityInfo().getActivityId());
                campData.setCampId(campId);
                campData.setMatchId(channelTonBingData.getMatchId());
                campData.setInsertOption();
                campMatchDataMap.put(campId, campData);
            } else {
                // 已有数据说明有问题...
                logger.error("ton bing initCampUserData, repeated create data? activityId {}, campId {}.", config.getActivityInfo().getActivityId(), campId);
                ChannelTonBingCampMatchData campData = campMatchDataMap.get(campId);
                campData.setCollectionMap(new ConcurrentHashMap<>());
                campMatchDataMap.put(campId, campData);
            }
        }
    }

    /**
     * 触发城池的采集收益 只触发对应阵营的
     * 玩家点击要领取的时候触发下，被打下来触发下，buff失效的时候触发下
     */
    private void makeAllCityCollect(int campId, long dealTime) {
        int turn = config.getTurn();
        if(turn > 0){
            long endTime = config.getStage1TimeList().get(turn - 1).get(1) + DateHelper.HOUR_MILLIONS;
            if(dealTime > endTime){
                dealTime = endTime;
            }
        }
        // 阵营空、未生成数据，不处理
        if (campId == 0 || !campMatchDataMap.containsKey(campId)) {
            return;
        }

        List<Integer> needDealCity = new ArrayList<>();
        long nowTime = dealTime;
        Map<Integer, ChannelTonBingCityConfig> cityConfigMap = config.getCityConfigMap(channelTonBingData.getWarType());
        Map<Integer, ChannelTonBingCity> cityMap = channelTonBingData.getCityMap();
        ChannelTonBingCampMatchData campData = campMatchDataMap.get(campId);

        synchronized (lock) {
            // 获取有收益的城池
            for (ChannelTonBingCity city : cityMap.values()) {
                if (city.getCampId() != campId) {
                    continue;
                }
                ChannelTonBingCityConfig cityConfig = cityConfigMap.get(city.getCityId());
                ChannelTonBingCityTypeConfig cityTypeConfig = config.getCityTypeConfigMap(channelTonBingData.getWarType()).get(cityConfig.getType());
                // 每小时产生一次收益
                if (cityTypeConfig.getFoodProduction() > 0 && nowTime - city.getCollectionTime() >= DateHelper.HOUR_MILLIONS) {
                    needDealCity.add(city.getCityId());
                }
            }
            // 城池加采集奖励
            Map<Long, Map<Integer, Long>> collectionMap = campData.getCollectionMap();
            for (int cityId : needDealCity) {
                // 计算产量
                long productValue = getCityFoodProduct(cityId, nowTime);
                // 给所有玩家加
                addAllUserCityProductReward(campId, cityId, productValue, collectionMap);
            }
            if (needDealCity.size() > 0) {
                campData.setUpdateOption();
            }
        }

        if (!needDealCity.isEmpty()) {
            // 红点
            for (Map.Entry<Long, Map<Integer, Long>> entry : campData.getCollectionMap().entrySet()) {
                long userId = entry.getKey();
                long totalValue = 0;
                for (Long value : entry.getValue().values()) {
                    totalValue += value;
                }
                if (userId > 0 && totalValue > config.getRedDotCollectCount()) {
                    addRedDot(getUserData(userId), eTonBingRedDotType.Type10_GetCollect);
                }
            }
        }
    }


    public Map<Integer,Long> getCampMaxZhanGongUnionUserIdMap() {
        Map<Integer,Long> campIdUserIdMap = new ConcurrentHashMap<>();
        for(int campId : campUserRankMap.keySet()){
            long userId = 0;
            ChannelTonBingRankListModel rankListModel = campUnionRankMap.get(campId);
            if (rankListModel != null) {
                LinkedList<ChannelTonBingRank> rankList = rankListModel.getRankList();
                if (!rankList.isEmpty()) {
                    ChannelTonBingRank first = rankList.getFirst();
                    ChannelTonBingUnionJoin join = getUnionJoin(first.getId());
                    if(join != null){
                        for(ChannelTonBingUnionJoinMember member : join.getMemberMap().values()){
                            if(member.getPosition() == eUnionPosition.Master.getValue()){
                                userId = member.getUserId();
                                break;
                            }
                        }
                    }
                }
            }
            campIdUserIdMap.put(campId,userId);
            if(userId != 0){
                ChannelTonBingUserData userData = getUserData(userId);
                Map<Integer, Integer> posMap = userData.getPositionMap();
                posMap.put(userData.getCampId(),eTonBingPosition.Master.getValue());
                Map<Integer,Integer> appointMap = new ConcurrentHashMap<>();
                appointMap.put(userData.getCampId(),eTonBingPosition.Master.getValue());
                userData.setPositionMap(posMap);
                UserBaseInfo userBaseInfo = ChannelUserMgr.getUserBaseInfo(userId);
                if (userBaseInfo != null) {
                    ChannelTonBingProto.ChannelTonBingCityWinerAppointRespMsg.Builder crespMsg = ChannelTonBingProto.ChannelTonBingCityWinerAppointRespMsg.newBuilder();
                    crespMsg.setWinerName("");
                    parsePosAppoint(crespMsg,appointMap);
                    crespMsg.setCampId(userData.getCampId());
                    crespMsg.setSendReward(true);
                    sendChannelPacket(GameProtocol.S_CHANNEL_CTON_BING_CITY_APPOINT_POSITION, crespMsg,userData);
                }
            }
        }
        return campIdUserIdMap;
    }

    /**
     * buff后产量
     */
    private long getCityFoodProductBuffPer(int cityId) {
        Map<Integer, ChannelTonBingCity> cityMap = channelTonBingData.getCityMap();
        ChannelTonBingCity cityData = cityMap.get(cityId);
        ChannelTonBingCityConfig cityConfig = config.getCityConfigMap(channelTonBingData.getWarType()).get(cityId);
        ChannelTonBingCityTypeConfig cityTypeConfig = config.getCityTypeConfigMap(channelTonBingData.getWarType()).get(cityConfig.getType());
        Integer buffAddition = getBuffAddition(cityData.getCampId(), eTonBingBuffType.Type5_CityFoodProduce).get(0);

        Integer rankBuffAddition = getBuffAddition(cityData.getCampId(), eTonBingBuffType.Type7_CityFoodProduce).get(0);
        buffAddition = buffAddition + rankBuffAddition;
        BigDecimal production = BigDecimal.valueOf(cityTypeConfig.getFoodProduction())
                .multiply(BigDecimal.valueOf(1000 + buffAddition)).divide(BigDecimal.valueOf(1000), 0, BigDecimal.ROUND_DOWN);
        return production.longValue();
    }

    // 城池指定时间的采集产量
    private long getCityFoodProduct(int cityId, Long nowTime) {
        Map<Integer, ChannelTonBingCity> cityMap = channelTonBingData.getCityMap();
        ChannelTonBingCity cityData = cityMap.get(cityId);
        // 收益多少次
        long times = (nowTime - cityData.getCollectionTime()) / DateHelper.HOUR_MILLIONS;
        cityData.setCollectionTime(cityData.getCollectionTime() + times * DateHelper.HOUR_MILLIONS);
        channelTonBingData.setUpdateOption();

        BigInteger allValue = BigInteger.valueOf(times).multiply(BigInteger.valueOf(getCityFoodProductBuffPer(cityId)));
        return allValue.longValue();
    }

    /**
     * 给同阵营所有玩家加采集奖励
     */
    private void addAllUserCityProductReward(int campId, int cityId, long productValue, Map<Long, Map<Integer, Long>> userCityValueMap) {
        for (ChannelTonBingUserData userData : userDataMap.values()) {
            if (!isAble(userData) || userData.getCampId() != campId) {
                continue;
            }

            if (!userCityValueMap.containsKey(userData.getUserId())) {
                userCityValueMap.put(userData.getUserId(), new ConcurrentHashMap<>());
            }
            Map<Integer, Long> cityValueMap = userCityValueMap.get(userData.getUserId());

            Long oldValue = cityValueMap.getOrDefault(cityId, 0L);
            cityValueMap.put(cityId, oldValue + productValue);
        }
    }

    /**
     * 城池采集界面信息
     */
    public void cityFoodInfo(long userId) {
        ChannelTonBingUserData userData = getUserData(userId);
        if (!isAble(userData)) {
            // 没资格的不可能打开采集界面
            return;
        }

        int campId = userData.getCampId();

        // 重新计算采集
        makeAllCityCollect(campId, System.currentTimeMillis());

        // 协议
        ChannelTonBingProto.CTonBingCityFoodRespMsg.Builder respMsg = ChannelTonBingProto.CTonBingCityFoodRespMsg.newBuilder();
        respMsg.setRet(0);

        Set<Integer> hadCitySet = new HashSet<>();

        ChannelTonBingCampMatchData campData = campMatchDataMap.get(campId);
        if (campData != null) {
            // 有收益的城池
            Map<Integer, Long> cityValueMap = campData.getCollectionMap().getOrDefault(userId, new ConcurrentHashMap<>());
            for (Map.Entry<Integer, Long> entry : cityValueMap.entrySet()) {
                int cityId = entry.getKey();
                long value = entry.getValue();

                if (value <= 0) {
                    continue; // 没收益
                }

                ChannelTonBingProto.CTonBingCityFoodMsg.Builder tempMsg = ChannelTonBingProto.CTonBingCityFoodMsg.newBuilder();
                tempMsg.setCityId(cityId);
                tempMsg.setFood(value);
                tempMsg.setFoodProduction(getCityFoodProductBuffPer(cityId)); // 产出速度
                respMsg.addFoods(tempMsg);

                hadCitySet.add(cityId);
            }
        }

        // 无收益但占领的城池
        for (ChannelTonBingCity city : getChannelTonBingData().getCityMap().values()) {
            if (hadCitySet.contains(city.getCityId()) || city.getCampId() != campId) {
                continue;
            }
            ChannelTonBingProto.CTonBingCityFoodMsg.Builder tempMsg = ChannelTonBingProto.CTonBingCityFoodMsg.newBuilder();
            tempMsg.setCityId(city.getCityId());
            tempMsg.setFood(0);
            tempMsg.setFoodProduction(getCityFoodProductBuffPer(city.getCityId()));
            respMsg.addFoods(tempMsg);
        }

        sendChannelPacket(ClientProtocol.U_CTON_BING_CITY_FOOD, respMsg, userData);
    }

    /**
     * 城池采集一键收获
     */
    public void cityFoodCollect(long userId) {

        ChannelTonBingProto.ChannelTonBingCityFoodCollectRespMsg.Builder builder = ChannelTonBingProto.ChannelTonBingCityFoodCollectRespMsg.newBuilder();
        builder.setRet(0);

        ChannelTonBingUserData userData = getUserData(userId);

        if (!isAble(userData)) {
            builder.setRet(GameErrorCode.E_TON_BING_NO_ABLE);
            sendChannelPacket(ClientProtocol.U_CTON_BING_CITY_FOOD_COLLECT, builder, userData);
            return; // 无资格不可能进去点一键收获
        }

        // 重新计算收益
        makeAllCityCollect(userData.getCampId(), System.currentTimeMillis());

        // 统计总奖励
        long userAllValue = 0L;
        ChannelTonBingCampMatchData campData = campMatchDataMap.get(userData.getCampId());
        if (campData != null) {
            synchronized (lock) {
                Map<Long, Map<Integer, Long>> collectionMap = campData.getCollectionMap();
                Map<Integer, Long> cityValueMap = collectionMap.getOrDefault(userId, new ConcurrentHashMap<>());
                for (Long value : cityValueMap.values()) {
                    userAllValue += value;
                }
                collectionMap.put(userId, new ConcurrentHashMap<>()); // 清空
                campData.setUpdateOption();
            }
        }

        if (userAllValue <= 0) {
            builder.setRet(GameErrorCode.E_TON_BING_CITY_FOOD_COLLECT_NO_VALUE);
            sendChannelPacket(ClientProtocol.U_CTON_BING_CITY_FOOD_COLLECT, builder, userData);
            return; // 没奖励
        }

        String reward = config.getZiZhongItem() + "=" + userAllValue;
        builder.setReward(reward);
        sendChannelPacket(GameProtocol.S_CHANNEL_CTON_BING_CITY_FOOD_COLLECT, builder, userData);
    }

    private Map<Long, List<Long>> getCampServerIdUserIdMap(int campId) {
        Map<Long, List<Long>> resMap = new ConcurrentHashMap<>();
        for (ChannelTonBingUserData userData : new ConcurrentHashMap<>(userDataMap).values()) {
            if (userData.getCampId() == campId) {
                UserBaseInfo userBaseInfo = ChannelUserMgr.getUserBaseInfo(userData.getUserId());
                if (userBaseInfo != null) {
                    if (!resMap.containsKey(userBaseInfo.getServerId())) {
                        resMap.put(userBaseInfo.getServerId(), new ArrayList<>());
                    }
                    resMap.get(userBaseInfo.getServerId()).add(userData.getUserId());
                }
            }
        }
        return resMap;
    }

    public void initUserData(List<ChannelTonBingUserData> userDataList) {
        for(ChannelTonBingUserData userData : userDataList){
            userDataMap.put(userData.getUserId(),userData);
        }
        for(ChannelTonBingUserData userData : userDataList){
            //初始0的也要记录
//            addZhanGong(userData.getUserId(),0,0,channelTonBingData.getMatchId());
            //没有初始化一个，0也要榜单显示
            Map<Integer, Long> zgMap = userData.getZhanGongMap();
            if(!zgMap.containsKey(channelTonBingData.getMatchId())){
                zgMap.put(channelTonBingData.getMatchId(),0L);
            }
            userData.setZhanGongMap(zgMap);
            Map<Integer, ChannelTonBingTeam> teamMap = userData.getTeamMap();
            for(ChannelTonBingTeam team : teamMap.values()){
                team.setCityId(0);
                team.setTargetCityId(0);
                team.setSoldierNum(team.getMaxSoldier());
            }
            userData.setTeamMap(teamMap);
            //清掉所有红点
            Map<Integer, Long> redDotMap = userData.getRedDotMap();
            redDotMap.remove(eTonBingRedDotType.Type1_NpcEvent.getValue());
            redDotMap.remove(eTonBingRedDotType.Type2_UserWarReport.getValue());
            redDotMap.remove(eTonBingRedDotType.Type3_CityWarReport.getValue());
            redDotMap.remove(eTonBingRedDotType.Type4_BarracksFree.getValue());
            redDotMap.remove(eTonBingRedDotType.Type6_Wounded.getValue());
            redDotMap.remove(eTonBingRedDotType.Type7_CityEventRefresh.getValue());
            redDotMap.remove(eTonBingRedDotType.Type10_GetCollect.getValue());
            userData.setRedDotMap(redDotMap);
        }
    }

    /**
     * 计算全部兵营
     */
    public void calcAllBarracks() {
        for (ChannelTonBingUserData userData : userDataMap.values()) {

            ChannelTonBingBarracks barracks = userData.getBarracks();
            if (isAble(userData) && barracks.getReadyMaxNum() > 0) {

                barracksCalc(userData.getUserId());

                // 红点
                if (barracks.getCreateNum() == 0 && barracks.getReadyNum() < barracks.getReadyMaxNum()) {
                    addRedDot(userData, eTonBingRedDotType.Type4_BarracksFree);
                }
                if (barracks.getWoundedNum() == 0 && barracks.getRescueNum() > 0
                        || barracks.getRescueNum() > config.getRedDotCureNum()) {
                    addRedDot(userData, eTonBingRedDotType.Type6_Wounded);
                }
            }
        }
    }

    /**
     * 重新计算最大兵营数，来触发时候要有buff，开始和失效时候触发 Type6_ReadySoldierMaxNum
     */
    private void recalculateBarracksReadyMaxNum(long userId) {
        ChannelTonBingUserData userData = getUserData(userId);
        ChannelTonBingBarracks barracks = userData.getBarracks();
        barracks.setReadyMaxNum(getBarracksReadyMaxNum(userId));
        userData.setUpdateOption();
    }

    /**
     * 重新计算城池产量，开始的时候buff不能有，结束的时候buff不能失效 Type5_CityFoodProduce
     *
     * @param campId
     * @param dealTime buff开始或者结束的时间点
     */
    private void recalculateCityFoodProdProduction(int campId, long dealTime) {
        makeAllCityCollect(campId, dealTime);
    }

    /**
     * 重新计算医院最大伤兵数，来触发时候要有buff，开始和失效时候触发 Type4_Hospital
     *
     * @param userId
     */
    private void recalculateBarracksHospitalWoundedMaxNum(long userId) {
        ChannelTonBingUserData userData = getUserData(userId);
        ChannelTonBingBarracks barracks = userData.getBarracks();
        barracks.setHospitalSize(getBarracksWoundedMaxNum(userId));
        userData.setUpdateOption();
    }

    /**
     * 重新计算每次治疗伤兵数
     */
    private void recalculateBarracksHospitalWoundedCount(long userId) {
        ChannelTonBingUserData userData = getUserData(userId);
        ChannelTonBingBarracks barracks = userData.getBarracks();
        barracks.setRescueCount(getBarracksWoundedCount(userId));
        userData.setUpdateOption();
    }

    /**
     * 重新计算医院恢复伤兵，开始的时候buff不能有，结束的时候buff不能失效 Type4_Hospital
     */
    private void recalculateBarracksHospitalRecovery(long userId) {
        barracksCalc(userId);
    }

    /**
     * 计算军营预备兵上限
     */
    private long getBarracksReadyMaxNum(long userId) {
        // 拿队伍带兵上限总和
        long allValue = 0L;
        ChannelTonBingUserData userData = getUserData(userId);
        for (ChannelTonBingTeam team : userData.getTeamMap().values()) {
            allValue = allValue + team.getMaxSoldier();
        }
        // 乘以参数
        allValue = allValue * config.getBarrackLimitParam() / 1000;
        // 加buff
        Integer buffAddition = getBuffAddition(userData.getUserId(), eTonBingBuffType.Type6_ReadySoldierMaxNum).get(0);
        allValue = BigDecimal.valueOf(allValue)
                .multiply(BigDecimal.valueOf(1000 + buffAddition)).divide(BigDecimal.valueOf(1000), 0, BigDecimal.ROUND_DOWN).longValue();
        return allValue;
    }

    //获取最大的军营医院人数
    private long getBarracksWoundedMaxNum(long userId) {
        long allValue = config.getWoundedSoldierMaxNum();
        ChannelTonBingUserData userData = getUserData(userId);
        Integer buffAddition = getBuffAddition(userData.getUserId(), eTonBingBuffType.Type4_Hospital).get(0);
        allValue = BigDecimal.valueOf(allValue)
                .multiply(BigDecimal.valueOf(1000 + buffAddition)).divide(BigDecimal.valueOf(1000), 0, BigDecimal.ROUND_DOWN).longValue();
        return allValue;
    }

    // 获得伤兵恢复个数
    private long getBarracksWoundedCount(long userId) {
        //伤兵加上buff修复速度
        Integer buffAddition = getBuffAddition(userId, eTonBingBuffType.Type4_Hospital).get(1);
        return BigDecimal.valueOf(config.getWoundedRescueCount())
                .multiply(BigDecimal.valueOf(1000 + buffAddition))
                .divide(BigDecimal.valueOf(1000), 0, BigDecimal.ROUND_DOWN).longValue();
    }

    public boolean containsUser(long userId){
        return userDataMap.containsKey(userId);
    }

    public ChannelTonBingUserData getUserData(long userId) {
        ChannelTonBingUserData userData = userDataMap.get(userId);
        if(userData == null){
//            logger.error("比赛中出现找不到玩家信息{}",userId);
        }
        return userData;
    }

    public Map<Long,ChannelTonBingUserData> getUserDataMap() {
        return userDataMap;
    }

    public ChannelTonBingUnionJoin getUnionJoin(String unionUid) {
        return unionJoinMap.get(unionUid);
    }

    public ChannelTonBingData getChannelTonBingData() {
        return channelTonBingData;
    }

    // ============================== 定时器 ==============================

    /**
     * 初始化所有Task
     */
    public void initTask() {

        // 空事件
        addTask(new ChannelTonBingEmptyTask(this, System.currentTimeMillis() + DateHelper.SECOND_MILLIONS * 10));

        // 阶段切换移动玩家
        long stage1Begin = config.getStage1TimeList().get(channelTonBingData.getTurn() - 1).get(0);
        addTask(new ChannelTonBingInitTeamPosTask(this, stage1Begin));

        // 城池事件刷新
        long refreshTime = getNextCityEventTime(getChannelTonBingData().getRefreshCityEventTime(), true);
        addTask(new ChannelTonBingRefreshCityEventTask(this, refreshTime));

        // 城池事件生效
        long applyTime = getNextCityEventTime(getChannelTonBingData().getApplyCityEventTime(), false);
        addTask(new ChannelTonBingApplyCityEventTask(this, applyTime));

        // Buff结束
        for (List<ChannelTonBingCampBuff> buffList : getChannelTonBingData().getCampBuffMap().values()) {
            for (ChannelTonBingCampBuff buff : buffList) {
                addTask(new ChannelTonBingBuffExpireTask(this, getBuffEndTime(buff)));
            }
        }

        // 战斗事件
        for (ChannelTonBingCity city : getChannelTonBingData().getCityMap().values()) {
            if (city.getBattlefield() != null) {
                addTask(new ChannelTonBingBattleTask(this, calcNextBattleTime(city), city.getCityId()));
            }
        }

        // 移动事件
        for (ChannelTonBingUserData userData : userDataMap.values()) {
            if (isAble(userData)) {
                for (ChannelTonBingTeam team : userData.getTeamMap().values()) {
                    if (!team.getRouteList().isEmpty()) {
                        addTask(new ChannelTonBingMoveTask(this, team.getArriveTime(), userData.getUserId(), team.getOccupation()));
                    }
                }
            }
        }

        // 城防军恢复
        for (ChannelTonBingCity city : channelTonBingData.getCityMap().values()) {
            if (city.getTeamRecoveryBeginTime() != 0) {
                addTask(new ChannelTonBingCityTeamRecoveryTask(this, city.getTeamRecoveryBeginTime() + config.getCityTeamRecoveryTime(), city.getCityId()));
            }
            if (city.getCampId() != 0) {
                addTask(new ChannelTonBingRecoveryExtraTeamTask(this, getNextRecoveryExtraTeamTime(city), city.getCityId()));
            }
        }

        // 发商会荣誉（可重复调用）
        addTask(new ChannelTonBingSendUnionHonorTask(this, config.getActivityInfo().getEndTime() * 1000L + DateHelper.MINUTE_MILLIONS * 5));

        // 同步移动数据
        addTask(new ChannelTonBingSyncTeamTask(this));

        // 同步城池数据
        addTask(new ChannelTonBingSyncCityTask(this));

        // 设置城主
//        addTask(new ChannelTonBingSetWinnerTask(this, config.getActivityInfo().getEndTime() * 1000L));
    }

    /**
     * 空事件，10秒一次
     */
    public void channelTonBingEmptyTask() {
        addTask(new ChannelTonBingEmptyTask(this, System.currentTimeMillis() + DateHelper.SECOND_MILLIONS * 10));
    }

    /**
     * 初始化队伍位置
     */
    public void channelTonBingInitTeamPosTask() {
        long now = System.currentTimeMillis();

        // 【没锁榜】过会儿再看看
        if (!getChannelTonBingData().isLockUnion()) {
            addTask(new ChannelTonBingInitTeamPosTask(this, now + DateHelper.SECOND_MILLIONS * 10));
            return;
        }

        // 【强制移动没city的队伍】
        for (ChannelTonBingUserData userData : userDataMap.values()) {
            if (userData.getCampId() == 0) {
                continue; // 没阵营跳过
            }
            for (ChannelTonBingTeam team : userData.getTeamMap().values()) {
                if (team.getCityId() == 0) {
                    team.setRouteList(Collections.singletonList(getBirthCityId(userData.getCampId())));
                    forceArriveNextCity(team, System.currentTimeMillis());
                    userData.setUpdateOption();
                }
            }
        }

        // 【定时检查一下】
        addTask(new ChannelTonBingInitTeamPosTask(this, now + DateHelper.MINUTE_MILLIONS * 5));
    }



    /**
     * 战斗任务
     */
    public void channelTonBingBattleTask(int cityId) {
        synchronized (lock) {

            // 是否没战斗了
            ChannelTonBingCity city = getChannelTonBingData().getCityMap().get(cityId);
            ChannelTonBingBattlefield battlefield = city.getBattlefield();
            if (battlefield == null) {
                return;
            }

            // 时间到了没有
            long battleTime = calcNextBattleTime(city);
            if (System.currentTimeMillis() < battleTime) {
                addTask(new ChannelTonBingBattleTask(this, battleTime, cityId));
                return;
            }

            // 处理战斗
            List<ChannelTonBingBattleResult> resultList = new ArrayList<>();
            for (ChannelTonBingBattlefieldPart part : battlefield.getPartMap().values()) {
                ChannelTonBingBattleResult battleResult = handleBattle(city, battlefield, part, battleTime);
                if (battleResult != null) {
                    resultList.add(battleResult);
                }
            }

            // 设置上次战斗时间
            int drumTimes = battlefield.getDrumTimes();
            if (drumTimes > 0) {
                battlefield.setDrumTimes(drumTimes - 1);
            }
            city.setBattledNum(city.getBattledNum() + resultList.size());
            battlefield.setBattledNum(battlefield.getBattledNum() + resultList.size());
            battlefield.setLastBattleTime(battleTime);

            // 士兵进入战场
            teamToBattlefield(city, false);

            // 移除空战场
            List<Integer> removePartId = removeEmptyBattlefield(city);

            // 判断城池战报
            ChannelTonBingCityWarReport cityWarReport = null;
            if (battlefield.getPartMap().isEmpty()) {
                cityWarReport = recordBattleReport(city, battleTime); // 记录战报
            }

            // 同步战斗结果
            syncBattleResultMsg(resultList, cityWarReport);

            // 战斗结束处理
            if (cityWarReport != null) {
                handleBattleFinish(city, battleTime); // 判断城池换主人
                city.setBattlefield(null); // 移除战场
                chatLogic.clearBattlefieldChat(cityId); // 清空战场聊天
            }

            // 判断新的大战场
            if (city.getAtkTeamList().size() > 0 && city.getBattlefield() == null) {
                createBattlefield(city);
            }

            // 是否继续战斗
            if (city.getBattlefield() != null) {
                addTask(new ChannelTonBingBattleTask(this, calcNextBattleTime(city), city.getCityId()));
            } else {
                syncCityTeamToSelf(city);
            }

            channelTonBingData.setUpdateOption();

            // 同步城池
            addNeedSyncCity(cityId);

            // 清空场景
            for (Integer partId : removePartId) {
                ChannelSceneMgr.removeScene(config.getActivityInfo().getActivityId(), eSceneType.TonBingBattlefield, channelTonBingData.getMatchId() + "-" + getBattlefieldSceneKey(cityId, partId));
            }
        }
    }

    /**
     * 移动任务
     */
    public void channelTonBingMoveTask(long userId, int teamId) {
        ChannelTonBingUserData userData = getUserData(userId);
        ChannelTonBingTeam team = userData.getTeamMap().get(teamId);
        synchronized (lock) {

            // 判断状态
            if (getTeamStatus(team) != eTonBingTeamStatus.Move) {
                return;
            }

            long now = System.currentTimeMillis();

            // 时间没到
            if (now < team.getArriveTime()) {
                addTask(new ChannelTonBingMoveTask(this, team.getArriveTime(), userId, teamId));
                return;
            }

            // 队伍到达城池
            forceArriveNextCity(team, team.getArriveTime());
        }
    }

    /**
     * 刷新城池任务
     */
    public void channelTonBingRefreshCityEventTask() {

        boolean create = false;
        long refreshTime;

        synchronized (lock) {
            ChannelTonBingData channelTonBingData = getChannelTonBingData();

            long now = System.currentTimeMillis();
            refreshTime = getNextCityEventTime(channelTonBingData.getRefreshCityEventTime(), true);

            // 时间还没到
            if (now < refreshTime) {
                addTask(new ChannelTonBingRefreshCityEventTask(this, refreshTime));
                return;
            }

            // 刷新前，移除旧的
            Map<Integer, ChannelTonBingCityEvent> cityEventMap = channelTonBingData.getCityEventMap();
            for (Map.Entry<Integer, ChannelTonBingCityEvent> entry : cityEventMap.entrySet()) {
                ChannelTonBingCityEvent event = entry.getValue();
                if (event.getEffectCamp() != 0 && refreshTime > event.getEndTime()) {
                    cityEventMap.remove(entry.getKey());
                }
            }

            // 看哪些buff没被用掉
            List<Integer> allCampKey = new ArrayList<>();
            for (int i = 1; i <= config.getCampNum(); i++) {
                allCampKey.add(i);
            }
            for (ChannelTonBingCityEvent cityEvent : cityEventMap.values()) {
                // 未生效
                if (cityEvent.getEffectCamp() == 0) {
                    cityEvent.setRefreshTime(refreshTime); // 重设刷新时间
                    allCampKey.remove(Integer.valueOf(cityEvent.getCampKey()));
                }
            }

            // 【生成】
            for (Integer campKey : allCampKey) {
                List<Integer> cityIdList = getMiddleCityId(campKey, campKey == config.getCampNum() ? 1 : campKey + 1);

                // 尽量不相同
                List<Integer> newCityIdList = new ArrayList<>(cityIdList);
                newCityIdList.removeAll(cityEventMap.values().stream().map(ChannelTonBingCityEvent::getCityId).collect(Collectors.toList()));

                List<Integer> list = newCityIdList.size() > 0 ? newCityIdList : cityIdList;
                // 移除掉中心城和主城
                for (ChannelTonBingCityConfig config : config.getCityConfigMap(channelTonBingData.getWarType()).values()) {
                    if (config.getType() == 1 || config.getType() == 6) {
                        list.remove(Integer.valueOf(config.getId()));
                    }
                }
                // 不让他报错
                if (list.isEmpty()) {
                    for (int i = 0; i < 10; i++)
                        logger.error("activityId = {}, camp key = {}, mid city is empty!!!",
                                config.getActivityInfo().getActivityId(), campKey);
                    continue;
                }

                // 生成事件
                Integer cityId = list.get(randomHelper.next(list.size()));
                List<Integer> eventConfigList = config.getCityEventConfigList();
                int buffId = eventConfigList.get(randomHelper.next(eventConfigList.size()));
                cityEventMap.put(campKey, new ChannelTonBingCityEvent(campKey, cityId, buffId, refreshTime));

                create = true;
            }

            // 设置下次刷新
            channelTonBingData.setRefreshCityEventTime(refreshTime);
            addTask(new ChannelTonBingRefreshCityEventTask(this, getNextCityEventTime(refreshTime, true)));

            channelTonBingData.setUpdateOption();
        }

        // 同步在线玩家
        syncCityEventToAll();
        // 红点全亮
        for (ChannelTonBingUserData userData : userDataMap.values()) {
            if (isAble(userData)) {
                addRedDot(userData, eTonBingRedDotType.Type7_CityEventRefresh);
            }
        }

        // 日志
        for (ChannelTonBingCityEvent cityEvent : getChannelTonBingData().getCityEventMap().values()) {
            ChannelAutoLogMgr.add(new LogChannelTonBingCityEvent(
                    config.getActivityInfo().getActivityId(),
                    1,
                    refreshTime,
                    cityEvent.getCityId(),
                    JSON.toJSONString(cityEvent),
                    channelTonBingData.getMatchId(),
                    channelTonBingData.getTurn(),
                    channelTonBingData.getWarType()
            ));
        }

    }

    //开始下一局上一句的buff生效 cmapId Rank
    public void saveLastRank(Map<Integer,Integer> campRankMap){
        channelTonBingData.setRankBuffMap(campRankMap);
    }

    private ChannelTonBingBuffTypeConfig getRankBuffInfo(int campId){
        Map<Integer, Integer> rankMap = channelTonBingData.getRankBuffMap();
        int buffId = 0;
        int rank = rankMap.getOrDefault(campId,0);
        List<ActivityConditionInfo> conditionInfoList = ChannelActivityInfoMgr.getConditionInfoList(config.getActivityInfo().getActivityId(),eGamePlayerEventType.TonBingCampScoreRank.getValue());
        for(ActivityConditionInfo info : conditionInfoList){
            if(info.getType() == eGamePlayerEventType.TonBingCampScoreRank.getValue()){
                if(info.getParamList().get(0).intValue() == rank){
                    buffId = info.getParamList().get(3).intValue();
                }
            }
        }
        if(buffId != 0){
            return config.getBuffTypeConfigMap().get(buffId);
        }
        return null;
    }

    /**
     * 生效城池任务
     */
    public void channelTonBingApplyCityEventTask() {

        long applyTime;

        synchronized (lock) {

            ChannelTonBingData channelTonBingData = getChannelTonBingData();

            long now = System.currentTimeMillis();
            applyTime = getNextCityEventTime(channelTonBingData.getApplyCityEventTime(), false);

            // 时间还没到
            if (now < applyTime) {
                addTask(new ChannelTonBingApplyCityEventTask(this, applyTime));
                return;
            }

            boolean change = false;
            Map<Integer, ChannelTonBingCityEvent> cityEventMap = channelTonBingData.getCityEventMap();

            // 生效buff
            for (ChannelTonBingCityEvent cityEvent : cityEventMap.values()) {

                // 跳过已生效的buff
                if (cityEvent.getEffectCamp() != 0) {
                    continue;
                }

                ChannelTonBingCity city = channelTonBingData.getCityMap().get(cityEvent.getCityId());
                if (city.getCampId() != 0) {

                    ChannelTonBingCampBuff buff = new ChannelTonBingCampBuff(
                            city.getCampId(),
                            applyTime,
                            city.getCityId(),
                            cityEvent.getBuffId()
                    );

                    // buff生效前操作
                    beforeBeginBuff(buff);

                    // 添加buff结束任务
                    long buffEndTime = getBuffEndTime(buff);
                    addTask(new ChannelTonBingBuffExpireTask(this, buffEndTime));

                    // 加buff
                    Map<Integer, List<ChannelTonBingCampBuff>> campBuffMap = channelTonBingData.getCampBuffMap();
                    ArrayList<ChannelTonBingCampBuff> list = new ArrayList<>(campBuffMap.getOrDefault(city.getCampId(), new ArrayList<>()));
                    list.add(buff);
                    campBuffMap.put(city.getCampId(), list);

                    // 记录生效
                    cityEvent.setEffectCamp(city.getCampId());
                    cityEvent.setEndTime(getBuffEndTime(buff));

                    // buff生效后操作
                    afterBeginBuff(buff);

                    change = true;
                }
            }

            // 改时间
            channelTonBingData.setApplyCityEventTime(applyTime);
            // 下个任务
            addTask(new ChannelTonBingApplyCityEventTask(this, getNextCityEventTime(channelTonBingData.getApplyCityEventTime(), false)));

            syncCityEventToAll();
            syncBuffToAll();

            channelTonBingData.setUpdateOption();
        }

        // 日志
        for (ChannelTonBingCityEvent cityEvent : getChannelTonBingData().getCityEventMap().values()) {
            ChannelAutoLogMgr.add(new LogChannelTonBingCityEvent(
                    config.getActivityInfo().getActivityId(),
                    2,
                    applyTime,
                    cityEvent.getCityId(),
                    JSON.toJSONString(cityEvent),
                    channelTonBingData.getMatchId(),
                    channelTonBingData.getTurn(),
                    channelTonBingData.getWarType()
            ));
        }
    }

    /**
     * 过期失效buff
     */
    public void channelTonBingBuffExpireTask() {
        long now = System.currentTimeMillis();
        long minEndTime = Long.MAX_VALUE;
        Map<Integer, List<ChannelTonBingCampBuff>> campBuffMap = getChannelTonBingData().getCampBuffMap();
        for (Map.Entry<Integer, List<ChannelTonBingCampBuff>> entry : campBuffMap.entrySet()) {
            List<ChannelTonBingCampBuff> buffList = entry.getValue();
            for (int i = 0; i < buffList.size(); i++) {
                ChannelTonBingCampBuff campBuff = buffList.get(i);
                long endTime = getBuffEndTime(campBuff);
                if (now >= endTime) {
                    // 结束buff
                    beforeEndBuff(campBuff);
                    buffList.remove(i--);
                    afterEndBuff(campBuff);
                } else {
                    minEndTime = Math.min(endTime, minEndTime);
                }
            }
        }
        if (minEndTime != Long.MAX_VALUE) {
            addTask(new ChannelTonBingBuffExpireTask(this, minEndTime));
        }

        getChannelTonBingData().setUpdateOption();
    }

    /**
     * 城防军恢复
     */
    public void channelTonBingCityTeamRecoveryTask(int cityId) {
        synchronized (lock) {

            long now = System.currentTimeMillis();

            ChannelTonBingCity city = getChannelTonBingData().getCityMap().get(cityId);
            if (city.getTeamRecoveryBeginTime() == 0) {
                // 恢复过了，或者被刷新了
                return;
            }

            long recoveryTime = city.getTeamRecoveryBeginTime() + config.getCityTeamRecoveryTime();
            if (now < recoveryTime) {
                // 时间还没到
                addTask(new ChannelTonBingCityTeamRecoveryTask(this, recoveryTime, cityId));
                return;
            }

            if (city.getBattlefield() != null) {
                // 正在战斗
                city.setTeamRecoveryBeginTime(0);
                return;
            }

            ChannelTonBingCityConfig cityConfig = config.getCityConfigMap(channelTonBingData.getWarType()).get(cityId);
            ChannelTonBingCityTypeConfig cityTypeConfig = config.getCityTypeConfigMap(channelTonBingData.getWarType()).get(cityConfig.getType());

            int num = cityTypeConfig.getTeamNumTurn(channelTonBingData.getTurn()) + getBuffAddition(city.getCampId(), eTonBingBuffType.Type3_CityTeamMaxNum).get(0);

            // 恢复
            if (city.getTeamNum() < num) {
                city.setTeamNum(num);
            }
            city.setTeamRecoveryBeginTime(0);
            // 战斗场次清空
            city.setBattledNum(0);
            getChannelTonBingData().setUpdateOption();
        }
    }

    /**
     * 获取下次恢复额外守军的时间
     */
    private long getNextRecoveryExtraTeamTime(ChannelTonBingCity city) {
        // 间隔一小时
        long lastRecoveryExtraTeamTime = city.getLastRecoveryExtraTeamTime() == 0 ? config.getStage1TimeList().get(channelTonBingData.getTurn() - 1).get(0) : city.getLastRecoveryExtraTeamTime();
        return lastRecoveryExtraTeamTime + DateHelper.HOUR_MILLIONS;
    }

    /**
     * 恢复额外守军
     */
    public void channelTonBingRecoveryExtraTeamTask(int cityId) {
        synchronized (lock) {
            long now = System.currentTimeMillis();
            ChannelTonBingCity city = getChannelTonBingData().getCityMap().get(cityId);
            long recoveryTime = getNextRecoveryExtraTeamTime(city);
            if (now < recoveryTime) {
                // 时间还没到
                addTask(new ChannelTonBingRecoveryExtraTeamTask(this, recoveryTime, cityId));
                return;
            }
            // 恢复额外守军
            int cityType = config.getCityConfigMap(channelTonBingData.getWarType()).get(cityId).getType();
            int extraTeamNum = config.getCityTypeConfigMap(channelTonBingData.getWarType()).get(cityType).getExtraTeamNum();
            city.setLastRecoveryExtraTeamTime(recoveryTime);
            city.setExtraTeamNum(city.getExtraTeamNum() + extraTeamNum);
            getChannelTonBingData().setUpdateOption();
            // 恢复的守军进入战场
            if (city.getBattlefield() != null) {
                teamToBattlefield(city, true);
            }
            // 下一次恢复时间
            addTask(new ChannelTonBingRecoveryExtraTeamTask(this, getNextRecoveryExtraTeamTime(city), city.getCityId()));
            logger.info("额外城防军恢复,cityId: {},当前额外城防军数量: {}", cityId, city.getExtraTeamNum());
        }
    }


    /**
     * 加商会旗帜
     */
    public void channelTonBingSendUnionHonorTask() {
        synchronized (lock) {

            long now = System.currentTimeMillis();

            // 超过时间了
            if (now > config.getActivityInfo().getEndShowTime() * 1000L) {
                return;
            }

            long getRewardTime = config.getActivityInfo().getEndTime() * 1000L + DateHelper.MINUTE_MILLIONS * 5;

            // 没到时间
            if (now < getRewardTime) {
                addTask(new ChannelTonBingSendUnionHonorTask(this, getRewardTime));
                return;
            }

            // 没人
            LinkedList<ChannelTonBingRank> rankList = getCampScoreRankList();
            if (rankList.isEmpty()) {
                logger.error("getCampScoreRankList is empty, can not add union honor !!!!!!!!!!");
                return;
            }

            // 没商会
            int campId = Integer.valueOf(rankList.getFirst().getId());
            ChannelTonBingRankListModel rankListModel = campUnionRankMap.get(campId);
            if (rankListModel == null || rankListModel.getRankList().isEmpty()) {
                logger.error("campUnionRankMap campId {} is null or empty, can not add union honor !!!!!!!!!!", campId);
                return;
            }
        }
    }

    /**
     * 同步队伍
     */
    public void channelTonBingSyncTeamTask() {
        addTask(new ChannelTonBingSyncTeamTask(this));
        syncNeedSyncTeam();
    }

    /**
     * 定时同步城池
     */
    public void channelTonBingSyncCityTask() {
        addTask(new ChannelTonBingSyncCityTask(this));
        syncNeedSyncCity();
    }



    // ============================== 榜单 ==============================

    /**
     * 初始化全部榜单
     */
    public void initAllRankList() {
        ChannelTonBingRankListModel userModel = new ChannelTonBingRankListModel();
        Map<Integer, ChannelTonBingRankListModel> campUserMap = new ConcurrentHashMap<>();
        Map<Integer, ChannelTonBingRankListModel> campUnionMap = new ConcurrentHashMap<>();

        for (ChannelTonBingUserData userData : userDataMap.values()) {
            if (isAble(userData)) {
                ChannelTonBingRank rank = new ChannelTonBingRank(String.valueOf(userData.getUserId()), userData.getZhanGong(), userData.getZhanGongTime());
                // 【个人榜】
                userModel.rankChange(rank);
                // 【阵营内个人榜】
                campUserMap.putIfAbsent(userData.getCampId(), new ChannelTonBingRankListModel());
                campUserMap.get(userData.getCampId()).rankChange(rank);
            }
        }


        for (ChannelTonBingUnionJoin join : unionJoinMap.values()) {
            if (join.getCampId() != 0) {
                // 【阵营内商会榜】
                campUnionMap.putIfAbsent(join.getCampId(), new ChannelTonBingRankListModel());
                ChannelTonBingRank rank = getUnionZhanGongRank(join);
                campUnionMap.get(join.getCampId()).rankChange(rank);
            }
        }

        userRank = userModel;
        campUserRankMap = campUserMap;
        campUnionRankMap = campUnionMap;
    }

    /**
     * 获取商会战功Rank
     */
    private ChannelTonBingRank getUnionZhanGongRank(ChannelTonBingUnionJoin join) {
        long unionValue = 0;
        long maxTime = 0;
        for (long memberId : join.getMemberMap().keySet()) {
            ChannelTonBingUserData memberData = getUserData(memberId);
            unionValue += memberData.getZhanGong();
            maxTime = Math.max(maxTime, memberData.getZhanGongTime());
        }

        ChannelTonBingRank rank = new ChannelTonBingRank(join.getUnionUid(), unionValue, maxTime);
        rank.setAbility(join.getTotalAbility());
        return rank;
    }

    private ChannelTonBingRank getUnionZhanGongRankThisMatch(ChannelTonBingUnionJoin join) {
        long unionValue = 0;
        long maxTime = 0;
        for (long memberId : join.getMemberMap().keySet()) {
            ChannelTonBingUserData memberData = getUserData(memberId);
            unionValue += memberData.getZhanGongMap().getOrDefault(channelTonBingData.getMatchId(),0L);
            maxTime = Math.max(maxTime, memberData.getZhanGongTime());
        }

        ChannelTonBingRank rank = new ChannelTonBingRank(join.getUnionUid(), unionValue, maxTime);
        rank.setAbility(join.getTotalAbility());
        return rank;
    }

    /**
     * 阵营积分榜单
     */
    public LinkedList<ChannelTonBingRank> getCampScoreRankList() {

        Map<Integer, Long> campScoreMap = new ConcurrentHashMap<>();
        Map<Integer, Long> updateTimeMap = new ConcurrentHashMap<>();

        Map<Integer, Long> campZhangongMap = new ConcurrentHashMap<>();
        for(ChannelTonBingUserData userData : userDataMap.values()){
            long oldValue = campZhangongMap.getOrDefault(userData.getCampId(),0L);
            campZhangongMap.put(userData.getCampId(),oldValue + userData.getZhanGongMap().getOrDefault(channelTonBingData.getMatchId(),0L));
        }
        for (ChannelTonBingCity city : getChannelTonBingData().getCityMap().values()) {
            int campId = city.getCampId();
            int cityId = city.getCityId();

            if (campId == 0) {
                continue;
            }

            // 统计积分
            int cityType = config.getCityConfigMap(channelTonBingData.getWarType()).get(cityId).getType();
            //当前是第几天的比赛
            int turn = config.getTurn();
            List<Integer> addScoreList = config.getCityTypeConfigMap(channelTonBingData.getWarType()).get(cityType).getCampPointList();
            long addScore = 0;
            if(addScoreList.size() > turn - 1){
                addScore = addScoreList.get(turn - 1);
            }else{
                addScore = addScoreList.get(0);
            }

            long oldScore = campScoreMap.getOrDefault(campId, 0L);
            campScoreMap.put(campId, oldScore + addScore);
            // 存一下时间
            if (city.getOccupyTime() > updateTimeMap.getOrDefault(campId, 0L)) {
                updateTimeMap.put(campId, city.getOccupyTime());
            }
        }

        LinkedList<ChannelTonBingRank> rankList = new LinkedList<>();
        for (Map.Entry<Integer, Long> entry : campScoreMap.entrySet()) {
            int campId = entry.getKey();
            String id = String.valueOf(campId);
            ChannelTonBingRank rank = new ChannelTonBingRank(id, entry.getValue(), updateTimeMap.getOrDefault(entry.getKey(), System.currentTimeMillis()));
            rankList.add(rank);
        }

        ChannelTonBingRankListModel model = new ChannelTonBingRankListModel();
        model.setRankCampList(rankList);

        return model.getRankList();
    }

    // ============================== 战斗 ==============================

    /**
     * 生成战场
     */
    private void createBattlefield(ChannelTonBingCity city) {
        ChannelTonBingTeamArrive arrive = city.getAtkTeamList().get(0);
        ChannelTonBingBattlefield battlefield = new ChannelTonBingBattlefield();
        battlefield.setCreateTime(arrive.getTime());
        battlefield.setLastBattleTime(arrive.getTime());
        city.setBattlefield(battlefield);
        city.setTeamRecoveryBeginTime(0); // 城防军恢复停止计时
        teamToBattlefield(city, true);
        addTask(new ChannelTonBingBattleTask(this, calcNextBattleTime(city), city.getCityId()));
    }

    /**
     * 生成分战场
     */
    private void createBattlefieldPart(ChannelTonBingCity city) {
        ChannelTonBingBattlefieldTeam atkTeam = getFirstAtkTeam(city, true);
        ChannelTonBingBattlefieldTeam defTeam = getFirstDefTeam(city, true);

        ChannelTonBingBattlefield battlefield = city.getBattlefield();
        int partId = battlefield.getMaxPartId() + 1;
        battlefield.setMaxPartId(partId);

        ChannelTonBingBattlefieldPart part = new ChannelTonBingBattlefieldPart();
        part.setPartId(partId);
        part.getAtkList().add(atkTeam);
        part.getDefList().add(defTeam);

        battlefield.getPartMap().put(partId, part);
    }

    /**
     * 该城市下次战斗时间
     */
    public long calcNextBattleTime(ChannelTonBingCity city) {
        ChannelTonBingBattlefield battlefield = city.getBattlefield();
        if (battlefield == null) {
            return 0;
        }

        // 上次战斗时间
        long lastBattleTime = battlefield.getLastBattleTime();

        // 第一次战斗等一分钟
        if (battlefield.getBattledNum() == 0) {
            return lastBattleTime + config.getBattlePrepareTime();
        }

        long battleCostTime = config.getBattleCostTime();
        // 有战鼓，就减为1秒（下一场生效，所以判断UseDrumTime）
        if (battlefield.getDrumTimes() > 0 && battlefield.getUseDrumTime() < lastBattleTime) {
//            int aaa = GoodsMgr.getGoodsInfo(config.getZhanGuItem()).getParamList().get(1).intValue();
            int aaa = config.getZhanguParam2();
            battleCostTime =  aaa * DateHelper.SECOND_MILLIONS;
        }

        return battlefield.getLastBattleTime() + battleCostTime;
    }

    /**
     * 队伍进战场
     */
    public void teamToBattlefield(ChannelTonBingCity city, boolean needSync) {

        // 记录有变化的战场，用于同步
        Set<ChannelTonBingBattlefieldPart> changePartSet = new HashSet<>();

        ChannelTonBingBattlefield battlefield = city.getBattlefield();

        // 【空战场先填人】
        for (ChannelTonBingBattlefieldPart part : battlefield.getPartMap().values()) {
            if (part.getAtkList().isEmpty()) {
                ChannelTonBingBattlefieldTeam team = getFirstAtkTeam(city, true);
                if (team != null) {
                    part.getAtkList().addLast(team);
                    changePartSet.add(part);
                }
            }
            if (part.getDefList().isEmpty()) {
                ChannelTonBingBattlefieldTeam team = getFirstDefTeam(city, true);
                if (team != null) {
                    part.getAtkList().addLast(team);
                    changePartSet.add(part);
                }
            }
        }

        // 【开新战场】
        for (int i = 0; i < 999; i++) {
            boolean hasAtk = getFirstAtkTeam(city, false) != null;
            boolean hasDef = getFirstDefTeam(city, false) != null;
            if (!hasAtk || !hasDef) {
                break; // 不满足两边都有队伍，无法开新战场
            }
            int curPartNum = battlefield.getPartMap().size();
            int maxPartNum = getMaxBattlefieldPartNum(city);
            if (curPartNum < maxPartNum) {
                createBattlefieldPart(city); // 没到战场最大数量，可以开新战场
                changePartSet.add(battlefield.getPartMap().get(battlefield.getMaxPartId()));
            }
        }

        Map<Integer, ChannelTonBingBattlefieldPart> partMap = battlefield.getPartMap();

        // 【其余进攻方填进去】
        for (int i = 0; i < 999; i++) {
            // 没人了就不用加了
            if (getFirstAtkTeam(city, false) == null) {
                break;
            }
            // 找最小人数的战场
            ChannelTonBingBattlefieldPart part = null;
            for (ChannelTonBingBattlefieldPart tmp : partMap.values()) {
                if (!battlefieldPartIsFull(tmp, false) &&
                        (part == null || tmp.getAtkList().size() < part.getAtkList().size())) {
                    part = tmp;
                }
            }
            if (part != null) {
                part.getAtkList().add(getFirstAtkTeam(city, true));
                changePartSet.add(part);
            } else {
                break;
            }
        }

        // 【其余防守方填进去】
        for (int i = 0; i < 999; i++) {
            // 没人了就不用加了
            if (getFirstDefTeam(city, false) == null) {
                break;
            }
            // 找最小人数的战场
            ChannelTonBingBattlefieldPart part = null;
            for (ChannelTonBingBattlefieldPart tmp : partMap.values()) {
                if (!battlefieldPartIsFull(tmp, true) &&
                        (part == null || tmp.getDefList().size() < part.getDefList().size())) {
                    part = tmp;
                }
            }
            if (part != null) {
                part.getDefList().add(getFirstDefTeam(city, true));
                changePartSet.add(part);
            } else {
                break;
            }
        }

        if (needSync) {
            // 同步队伍进入战场
            for (ChannelTonBingBattlefieldPart part : changePartSet) {
                syncTeamEnterBattlefield(city, battlefield, part);
            }
        }
    }

    /**
     * 战场是否填满
     */
    private boolean battlefieldPartIsFull(ChannelTonBingBattlefieldPart part, boolean isDef) {
        int count = isDef ? part.getDefList().size() : part.getAtkList().size();
        int deadCount = isDef ? part.getDefDeadCount() : part.getAtkDeadCount();
        boolean win = part.getWinType() == 1 && isDef || part.getWinType() == 2 && !isDef;

        /*
         *      战场举例：
         *
         *          2   3
         *      4   5
         *
         *      firstNo = 2
         *      lineFirstNo = 1
         *      endNo = 5
         *      endNo - lineFirstNo + 1 = x    ===>    战场内队伍已经占了x个格子
         */

        // 找第一行最左边的编号
        int firstNo = deadCount + (win ? 1 : 0) + 1;
        int lineFirstNo = (firstNo + 2) / 3 * 3 - 2;

        // 队列最后一个的编号
        int endNo = deadCount + count;

        return endNo - lineFirstNo + 1 >= 6;
    }

    /**
     * 获取第一个进攻队伍
     */
    public ChannelTonBingBattlefieldTeam getFirstAtkTeam(ChannelTonBingCity city, boolean remove) {
        if (city.getAtkTeamList().size() <= 0) {
            return null;
        }
        ChannelTonBingTeamArrive arrive;
        if (remove) {
            arrive = city.getAtkTeamList().removeFirst();
        } else {
            arrive = city.getAtkTeamList().getFirst();
        }
        return new ChannelTonBingBattlefieldTeam(arrive);
    }

    /**
     * 获取第一个防守队伍
     */
    public ChannelTonBingBattlefieldTeam getFirstDefTeam(ChannelTonBingCity city, boolean remove) {
        // 优先出战基础城防守军
        if (getSoldierTeamCount(city) > 0) {
            if (city.getTeamNum() > 0) {
                if (remove) {
                    city.setTeamNum(city.getTeamNum() - 1);
                }
                // 普通城防兵
                return new ChannelTonBingBattlefieldTeam(city.getTeamSoldierNum(), eTonBingSoldierType.COMMON_SOLDIER.getValue());
            } else {
                if (remove) {
                    city.setExtraTeamNum(city.getExtraTeamNum() - 1);
                }
                // 额外城防兵
                return new ChannelTonBingBattlefieldTeam(city.getTeamSoldierNum(), eTonBingSoldierType.EXTRA_SOLDIER.getValue());
            }
        }

        if (city.getDefTeamList().size() <= 0) {
            return null;
        }
        ChannelTonBingTeamArrive arrive;
        if (remove) {
            arrive = city.getDefTeamList().removeFirst();
        } else {
            arrive = city.getDefTeamList().getFirst();
        }
        return new ChannelTonBingBattlefieldTeam(arrive);
    }

    public void teamReturnWaitList(ChannelTonBingCity city, ChannelTonBingBattlefieldPart part) {
        // 进攻方
        LinkedList<ChannelTonBingBattlefieldTeam> atkList = part.getAtkList();
        while (atkList.size() > 0) {
            ChannelTonBingBattlefieldTeam team = atkList.removeLast();
            if (team.getTeam() != null) {
                city.getAtkTeamList().addFirst(team.getTeam());
            }
        }
        // 防守方
        LinkedList<ChannelTonBingBattlefieldTeam> defList = part.getDefList();
        while (defList.size() > 0) {
            ChannelTonBingBattlefieldTeam team = defList.removeLast();
            if (team.getTeam() != null) {
                city.getDefTeamList().addFirst(team.getTeam());
            } else if (team.getSoldierNum() > 0) {
                if (team.getSoldierType() == eTonBingSoldierType.COMMON_SOLDIER.getValue()) {
                    city.setTeamNum(city.getTeamNum() + 1);
                } else if (team.getSoldierType() == eTonBingSoldierType.EXTRA_SOLDIER.getValue()) {
                    city.setExtraTeamNum(city.getExtraTeamNum() + 1);
                }
            }
        }
    }

    /**
     * 最大战场数
     */
    public int getMaxBattlefieldPartNum(ChannelTonBingCity city) {
        int atkNum = city.getAtkTeamList().size();
        int defNum = city.getDefTeamList().size() + getSoldierTeamCount(city);
        ChannelTonBingBattlefield battlefield = city.getBattlefield();
        if (battlefield != null) {
            for (ChannelTonBingBattlefieldPart part : battlefield.getPartMap().values()) {
                atkNum += part.getAtkList().size();
                defNum += part.getDefList().size();
            }
        }

        int param = config.getBattlefieldPartParam();

        int num1 = atkNum / param;
        int num2 = defNum / param;

        int result = Math.min(num1, num2);

        if (result <= 0) {
            result = 1;
        }
        return result;
    }

    public void addZhanGong(long userId, long zhanGong, long battleTime,int matchId) {

        // 领奖期不能加战功
        if (battleTime >= config.getActivityInfo().getEndTime() * 1000) {
            return;
        }

        ChannelTonBingUserData userData = getUserData(userId);
        String userRankId = String.valueOf(userId);

        // （之前是否发光）
        int lightBeforeRank = Integer.MAX_VALUE;
        if (userData.getZhanGong() >= config.getLightNeedZhanGong()) {
            int rankNum = 0;
            for (ChannelTonBingRank tmp : userRank.getRankList()) {
                if (++rankNum > config.getLightRankNum()) {
                    break;
                }
                if (Objects.equals(tmp.getId(), userRankId)) {
                    lightBeforeRank = rankNum;
                    break;
                }
            }
        }

        // 加战功
        userData.setZhanGong(userData.getZhanGong() + zhanGong);
        Map<Integer, Long> zgMap = userData.getZhanGongMap();
        zgMap.put(matchId,zgMap.getOrDefault(matchId,0L) + zhanGong);
        userData.setZhanGongMap(zgMap);
        userData.setZhanGongTime(battleTime);

        // 个人榜
        ChannelTonBingRank rank = new ChannelTonBingRank(String.valueOf(userId), userData.getZhanGong(), battleTime);
        userRank.rankChange(rank);

        // 阵营内个人榜
        campUserRankMap.putIfAbsent(userData.getCampId(), new ChannelTonBingRankListModel());
        campUserRankMap.get(userData.getCampId()).rankChange(rank);

        // 阵营内商会榜
        ChannelTonBingUnionJoin join = unionJoinMap.get(userData.getUnionUid());
        if (join != null) {
            long unionValue = 0;
            for (long memberId : join.getMemberMap().keySet()) {
                ChannelTonBingUserData memberData = getUserData(memberId);
                unionValue += memberData.getZhanGong();
            }

            ChannelTonBingRank unionRank = new ChannelTonBingRank(join.getUnionUid(), unionValue, battleTime);
            unionRank.setAbility(join.getTotalAbility());
            //商会排行榜还要战场排，其他搞不懂，先没有吧，太乱了
            unionRank.setWarType(channelTonBingData.getWarType());

            campUnionRankMap.putIfAbsent(userData.getCampId(), new ChannelTonBingRankListModel());
            campUnionRankMap.get(userData.getCampId()).rankChange(unionRank);
        }

        // 之后是否发光
        int lightAfterRank = Integer.MAX_VALUE;
        if (userData.getZhanGong() >= config.getLightNeedZhanGong()) {
            int rankNum = 0;
            for (ChannelTonBingRank tmp : userRank.getRankList()) {
                if (++rankNum > config.getLightRankNum()) {
                    break;
                }
                if (Objects.equals(tmp.getId(), userRankId)) {
                    lightAfterRank = rankNum;
                    break;
                }
            }
        }

        // 发光
        if (lightAfterRank <= config.getLightRankNum()) {
            if (lightBeforeRank > config.getLightRankNum()) { // 新发光
                syncLight();
            } else if (lightAfterRank != lightBeforeRank) { // 旧发光，但光变了
                syncLight();
            }
        }

        // 通知游戏服更新condition成就
        notifyConditionChange(userId, eGamePlayerEventType.TonBingZhanGong, userData.getZhanGong());
        // 同步用户数据
        syncUserDataToGameServer(userData);
        ChannelTonBingMgr.getActivity(config.getActivityInfo().getActivityId()).rankZhanGong(userId,battleTime);
    }



    /**
     * 处理战斗
     */
    public ChannelTonBingBattleResult handleBattle(ChannelTonBingCity city,
                                                  ChannelTonBingBattlefield battlefield,
                                                  ChannelTonBingBattlefieldPart part,
                                                  long battleTime) {
        if (part.getAtkList().isEmpty() || part.getDefList().isEmpty()) {
            logger.error("battle error !!!!! cityId {} partId {}, see db log...", city.getCityId(), part.getPartId());
            return null;
        }
        ChannelTonBingBattlefieldTeam atk = part.getAtkList().getFirst();
        ChannelTonBingBattlefieldTeam def = part.getDefList().getFirst();

        // 阵营ID
        int atkCampId = getUserData(atk.getTeam().getUserId()).getCampId();
        int defCampId = city.getCampId();

        // 获取士兵数
        long defCount = def.getTeam() == null ? def.getSoldierNum() : getTeamByArrive(def.getTeam()).getSoldierNum();
        long atkCount = getTeamByArrive(atk.getTeam()).getSoldierNum();

        // 计算守军buff
        ChannelTonBingCityConfig cityConfig = config.getCityConfigMap(channelTonBingData.getWarType()).get(city.getCityId());
        ChannelTonBingCityTypeConfig cityTypeConfig = config.getCityTypeConfigMap(channelTonBingData.getWarType()).get(cityConfig.getType());
        int defAddition = cityTypeConfig.getAddition() + cityConfig.getSpecialAdditionMap().getOrDefault(defCampId, 0);
        int atkAddition = cityConfig.getSpecialAdditionMap().getOrDefault(atkCampId, 0);
        long defCount2 = defCount * (1000 + defAddition) / 1000;
        long atkCount2 = atkCount * (1000 + atkAddition) / 1000;

        // 判断胜利方
        int winType = defCount2 == atkCount2 ? 0 : (defCount2 > atkCount2 ? 1 : 2);

        // 损兵比例
        BigDecimal defCostPercent = winType != 1 ? BigDecimal.ONE : calcBattleCostPercent(defCount2, atkCount2);
        BigDecimal atkCostPercent = winType != 2 ? BigDecimal.ONE : calcBattleCostPercent(atkCount2, defCount2);

        // 主城战斗时，守军必胜
        if (cityConfig.getType() == 1) {
            winType = 1;
            defCostPercent = BigDecimal.ZERO;
            atkCostPercent = BigDecimal.ONE;
        }

        // 计算伤亡
        long defCost = BigDecimal.valueOf(defCount).multiply(defCostPercent).setScale(0, BigDecimal.ROUND_DOWN).longValue();
        long atkCost = BigDecimal.valueOf(atkCount).multiply(atkCostPercent).setScale(0, BigDecimal.ROUND_DOWN).longValue();

        // 扣伤亡 + 加伤兵
        if (def.getTeam() != null) {
            ChannelTonBingTeam defTeam = getTeamByArrive(def.getTeam());
            defTeam.setSoldierNum(defTeam.getSoldierNum() - defCost);
            long defWoundedNum = calcChangeWoundedSoldierNum(defCost);
            addBarracksWoundedSoldier(defTeam.getUserId(), defWoundedNum);
            getUserData(def.getTeam().getUserId()).setUpdateOption();
        } else {
            def.setSoldierNum(def.getSoldierNum() - defCost);
        }
        ChannelTonBingTeam atkTeam = getTeamByArrive(atk.getTeam());
        atkTeam.setSoldierNum(atkTeam.getSoldierNum() - atkCost);
        long atkWoundedNum = calcChangeWoundedSoldierNum(atkCost);
        addBarracksWoundedSoldier(atkTeam.getUserId(), atkWoundedNum);
        getUserData(atk.getTeam().getUserId()).setUpdateOption();

        // 失败方回城
        if (winType != 1 && def.getTeam() != null) {
            ChannelTonBingTeam team = getTeamByArrive(def.getTeam());
            team.setRouteList(Collections.singletonList(getBirthCityId(getUserData(team.getUserId()).getCampId())));
            forceArriveNextCity(team, battleTime);
            handleTargetCity(team);
        }
        if (winType != 2) {
            atkTeam.setRouteList(Collections.singletonList(getBirthCityId(getUserData(atkTeam.getUserId()).getCampId())));
            forceArriveNextCity(atkTeam, battleTime);
            handleTargetCity(atkTeam);
        }

        // 加战功
        long defZhanGong = calcZhanGong(defCost, defCount);
        if (def.getTeam() != null) {
            addZhanGong(def.getTeam().getUserId(), defZhanGong, battleTime,channelTonBingData.getMatchId());
        }
        long atkZhanGong = calcZhanGong(atkCost, atkCount);
        addZhanGong(atk.getTeam().getUserId(), atkZhanGong, battleTime,channelTonBingData.getMatchId());

        // 连胜
        int defWinTimes = def.getTeam() == null ? 0 : battlefield.getWinTimes().getOrDefault(getTeamKey(def.getTeam()), 0);
        int atkWinTimes = battlefield.getWinTimes().getOrDefault(getTeamKey(atk.getTeam()), 0);
        if (winType == 1) {
            if (def.getTeam() != null) battlefield.getWinTimes().put(getTeamKey(def.getTeam()), ++defWinTimes);
            battlefield.getWinTimes().remove(getTeamKey(atk.getTeam()));
        } else if (winType == 2) {
            if (def.getTeam() != null) battlefield.getWinTimes().remove(getTeamKey(def.getTeam()));
            if (def.getTeam() != null)
                battlefield.getWinTimes().put(getTeamKey(atk.getTeam()), ++atkWinTimes); // 打城防军不算连胜
        } else {
            if (def.getTeam() != null) battlefield.getWinTimes().remove(getTeamKey(def.getTeam()));
            battlefield.getWinTimes().remove(getTeamKey(atk.getTeam()));
        }
        // 连胜公告
        checkContinueWinReport(city.getCityId(), winType, battleTime,
                def, defWinTimes,
                atk, atkWinTimes);

        // 战场排行
        if (def.getTeam() != null) {
            battlefield.rankChange(def.getTeam().getUserId(), defZhanGong, atkCost);
        }
        battlefield.rankChange(atkTeam.getUserId(), atkZhanGong, defCost);

        // 记录战斗结果
        if (winType != 1) {
            part.getDefList().removeFirst();
            part.setDefDeadCount(part.getDefDeadCount() + 1);
        }
        if (winType != 2) {
            part.getAtkList().removeFirst();
            part.setAtkDeadCount(part.getAtkDeadCount() + 1);
        }
        part.setWinType(winType);

        // 记录个战报
        ChannelTonBingUserWarReport userReport = new ChannelTonBingUserWarReport();
        if (def.getTeam() == null) {
            userReport.setUserIdDef(0);
            userReport.setDefPatronsId(0);
        } else {
            ChannelTonBingTeam defTeam = getTeamByArrive(def.getTeam());
            userReport.setUserIdDef(defTeam.getUserId());
            userReport.setDefPatronsId(defTeam.getPatronsList().isEmpty() ? 0 : defTeam.getPatronsList().get(0));
        }
        userReport.setActivityId(config.getActivityInfo().getActivityId());
        userReport.setUserIdAct(atkTeam.getUserId());
        userReport.setActPatronsId(atkTeam.getPatronsList().isEmpty() ? 0 : atkTeam.getPatronsList().get(0));
        userReport.setDefSoldierNum(defCount);
        userReport.setActSoldierNum(atkCount);
        userReport.setDefAddition(defAddition);
        userReport.setActAddition(atkAddition);
        userReport.setDefZhanGong(defZhanGong);
        userReport.setActZhanGong(atkZhanGong);
        userReport.setDefDieSoldierNum(defCost);
        userReport.setActDieSoldierNum(atkCost);
        userReport.setWinUserId(winType);
        userReport.setTime(battleTime);
        userReport.setCityId(city.getCityId());
        addUserWarReport(userReport);

        // 返回结果
        return new ChannelTonBingBattleResult(
                part, battlefield, city,
                winType,
                def, defCost, defZhanGong,
                atk, atkCost, atkZhanGong,
                winType == 1 ? defWinTimes : winType == 2 ? atkWinTimes : 0
        );
    }

    /**
     * 处理战略目标操作
     */
    private void handleTargetCity(ChannelTonBingTeam team) {
        if (team.getTargetCityId() != 0) {
            if (!moveToTargetCity(team)) {
                // 如果兵力不足或者无法前往城池，取消战略目标
                team.setTargetCityId(0);
                // 队伍同步
                syncTeamToSelf(team.getUserId(), Collections.singletonList(team));
            }
        }
    }

    /**
     * 补满兵自动前往战略目标
     */
    private boolean moveToTargetCity(ChannelTonBingTeam team) {
        ChannelTonBingUserData userData = getUserData(team.getUserId());
        if (!isAble(userData)) {
            return false;
        }
        // 判断预备兵是否足够
        if (team.getMaxSoldier() > userData.getBarracks().getReadyNum()) {
            return false;
        }
        // 预备兵扣除
        userData.getBarracks().setReadyNum(userData.getBarracks().getReadyNum() - team.getMaxSoldier());
        // 补满兵力
        team.setSoldierNum(team.getMaxSoldier());
        userData.setUpdateOption();
        // 前往战略目标
        return moveToCity(team, team.getTargetCityId()) == 0;
    }

    /**
     * 检查连胜公告
     */
    private void checkContinueWinReport(int cityId, int winType, long time,
                                        ChannelTonBingBattlefieldTeam defTeam, int defWinTimes,
                                        ChannelTonBingBattlefieldTeam atkTeam, int atkWinTimes) {
        int num = config.getContinueWinReportTimes();

        // 拿昵称
        String defNickname;
        if (defTeam.getTeam() != null) {
            UserBaseInfo defInfo = ChannelUserMgr.getUserBaseInfo(defTeam.getTeam().getUserId());
            defNickname = defInfo == null ? "" : defInfo.getNickName();
        } else {
            defNickname = config.getCityDefTeamNameKey();
        }
        UserBaseInfo atkInfo = ChannelUserMgr.getUserBaseInfo(atkTeam.getTeam().getUserId());
        String atkNickname = atkInfo == null ? "" : atkInfo.getNickName();

        // 看连胜
        if (winType == 1 && defWinTimes >= num) {
            addSystemChat(eMessageType.TonBingBattlefieldWinChat, cityId,
                    1 + config.getTalkSeparator() + defNickname + config.getTalkSeparator() + defWinTimes + config.getTalkSeparator() + atkNickname + config.getTalkSeparator() + atkWinTimes
                    , time);
        }
        if (winType == 2 && atkWinTimes >= num) {
            addSystemChat(eMessageType.TonBingBattlefieldWinChat, cityId,
                    1 + config.getTalkSeparator() + atkNickname + config.getTalkSeparator() + atkWinTimes + config.getTalkSeparator() + defNickname + config.getTalkSeparator() + defWinTimes
                    , time);
        }
        // 看终结连胜
        if (winType != 1 && defWinTimes >= num) {
            addSystemChat(eMessageType.TonBingBattlefieldWinChat, cityId,
                    2 + config.getTalkSeparator() + atkNickname + config.getTalkSeparator() + atkWinTimes + config.getTalkSeparator() + defNickname + config.getTalkSeparator() + defWinTimes
                    , time);
        }
        if (winType != 2 && atkWinTimes >= num) {
            addSystemChat(eMessageType.TonBingBattlefieldWinChat, cityId,
                    2 + config.getTalkSeparator() + defNickname + config.getTalkSeparator() + defWinTimes + config.getTalkSeparator() + atkNickname + config.getTalkSeparator() + atkWinTimes
                    , time);
        }
    }

    /**
     * 计算转换伤兵数量
     */
    private long calcChangeWoundedSoldierNum(long deadNum) {
        return deadNum * config.getWoundedSoldierPercent() / 1000;
    }

    /**
     * 计算战功
     */
    private long calcZhanGong(long soldierCostNum, long mySoldierNum) {
        BigDecimal param1 = BigDecimal.valueOf(config.getZhanGongParam().get(0));
        BigDecimal param2 = BigDecimal.valueOf(config.getZhanGongParam().get(1));
        BigDecimal param3 = BigDecimal.valueOf(config.getZhanGongParam().get(2));
        BigDecimal param4 = BigDecimal.valueOf(config.getZhanGongParam().get(3));
        BigDecimal costNum = BigDecimal.valueOf(soldierCostNum);
        BigDecimal one = BigDecimal.ONE;
        BigDecimal myNum = BigDecimal.valueOf(mySoldierNum);

        BigDecimal part2 = myNum.divide(param4, 8, BigDecimal.ROUND_DOWN); // 我方兵数/参数4
        BigDecimal part3 = param1.divide(param2, 8, BigDecimal.ROUND_DOWN); // 参数1/参数2

        BigDecimal value = one
                .add(part2.multiply(part3))
                .multiply(costNum)
                .multiply(param3);

        return value.setScale(0, BigDecimal.ROUND_DOWN).longValue();
    }

    /**
     * 计算损兵比例
     */
    private BigDecimal calcBattleCostPercent(long myCount, long enemyCount) {
        // 容错
        myCount = myCount <= 0 ? 1 : myCount;
        enemyCount = enemyCount <= 0 ? 1 : enemyCount;

        BigDecimal param1 = BigDecimal.valueOf(config.getBattleParam().get(0));
        BigDecimal param2 = BigDecimal.valueOf(config.getBattleParam().get(1));
        BigDecimal param3 = BigDecimal.valueOf(config.getBattleParam().get(2));
        BigDecimal param4 = BigDecimal.valueOf(config.getBattleParam().get(3));
        BigDecimal param5 = BigDecimal.valueOf(config.getBattleParam().get(4));
        BigDecimal param6 = BigDecimal.valueOf(config.getBattleParam().get(5));
        BigDecimal param7 = BigDecimal.valueOf(config.getBattleParam().get(6));

        // 敌方兵数/我方兵数
        BigDecimal partSoldier = BigDecimal.valueOf(enemyCount).divide(BigDecimal.valueOf(myCount), 8, BigDecimal.ROUND_DOWN);
        // 参数1/参数2
        BigDecimal part12 = param1.divide(param2, 8, BigDecimal.ROUND_DOWN);
        // 参数3/参数4
        BigDecimal part34 = param3.divide(param4, 8, BigDecimal.ROUND_DOWN);
        // 参数5/参数6
        BigDecimal part56 = param5.divide(param6, 8, BigDecimal.ROUND_DOWN);

        BigDecimal partSoldier12 = BigDecimal.valueOf(Math.pow(partSoldier.doubleValue(), part12.doubleValue())).setScale(8, BigDecimal.ROUND_DOWN);

        BigDecimal result = partSoldier12.multiply(part34)
                .add(partSoldier.multiply(part56))
                .add(param7)
                .multiply(partSoldier)
                .setScale(8, BigDecimal.ROUND_DOWN);

        // 不能超过1
        if (result.compareTo(BigDecimal.ONE) > 0) {
            result = BigDecimal.ONE;
        }

        return result;
    }

    /**
     * 记录城池战报
     */
    public ChannelTonBingCityWarReport recordBattleReport(ChannelTonBingCity city, long time) {

        // 城池战报数据准备
        int defCampId = city.getCampId();
        ChannelTonBingBattlefield battlefield = city.getBattlefield();
        LinkedList<ChannelTonBingBattlefieldRank> rankList = battlefield.getBattlefieldRankNewList();
        LinkedList<ChannelTonBingBattlefieldRank> atkRank = new LinkedList<>();
        LinkedList<ChannelTonBingBattlefieldRank> defRank = new LinkedList<>();
        Set<Integer> atkCampSet = new HashSet<>();
        for (ChannelTonBingBattlefieldRank rank : rankList) {
            int campId = getUserData(rank.getUserId()).getCampId();
            if (defCampId == campId) {
                if (defRank.size() < 3) {
                    defRank.add(rank);
                }
            } else {
                if (atkRank.size() < 3) {
                    atkRank.add(rank);
                }
                atkCampSet.add(campId);
            }
        }

        boolean defWin = city.getAtkTeamList().isEmpty();
        int winCampId = defCampId;
        if (!defWin) {
            ChannelTonBingTeamArrive first = city.getAtkTeamList().getFirst();
            winCampId = getUserData(first.getUserId()).getCampId();
        }

        // 加城池战报
        ChannelTonBingCityWarReport report = new ChannelTonBingCityWarReport();
        report.setActivityId(config.getActivityInfo().getActivityId());
        report.setAtkCampId(new ArrayList<>(atkCampSet));
        report.setDefCampId(defCampId);
        report.setType(defWin ? 2 : 1);
        report.setCityId(city.getCityId());
        report.setTime(battlefield.getLastBattleTime());
        report.setAtkRankList(atkRank);
        report.setDefRankList(defRank);
        report.setWinCampId(winCampId);
        report.setMatchId(channelTonBingData.getMatchId());
        addCityWarReport(report);

        return report;
    }

    /**
     * 处理城池战斗结束
     */
    public void handleBattleFinish(ChannelTonBingCity city, long time) {

        int defCampId = city.getCampId();

        ChannelTonBingBattlefield battlefield = city.getBattlefield();

        ChannelTonBingCityConfig cityConfig = config.getCityConfigMap(channelTonBingData.getWarType()).get(city.getCityId());
        ChannelTonBingCityTypeConfig typeConfig = config.getCityTypeConfigMap(channelTonBingData.getWarType()).get(cityConfig.getType());

        boolean defWin = city.getAtkTeamList().isEmpty();
        if (!defWin) {
            // 占领了
            ChannelTonBingTeamArrive first = city.getAtkTeamList().getFirst();
            int campId = getUserData(first.getUserId()).getCampId();

            // 同阵营队伍抽出来
            LinkedList<ChannelTonBingTeamArrive> atkList = new LinkedList<>();
            LinkedList<ChannelTonBingTeamArrive> defList = new LinkedList<>();
            for (ChannelTonBingTeamArrive arrive : new LinkedList<>(city.getAtkTeamList())) {
                if (campId == getUserData(arrive.getUserId()).getCampId()) {
                    defList.add(arrive);
                } else {
                    atkList.add(arrive);
                }
            }
            atkList.sort(Comparator.comparing(ChannelTonBingTeamArrive::getTime));
            defList.sort(Comparator.comparing(ChannelTonBingTeamArrive::getTime));

            // 拿buff
            ChannelTonBingCampBuff oldBuff = null;
            ChannelTonBingCampBuff newBuff = null;
            if (typeConfig.getBuffEffect() != 0) {
                oldBuff = city.getCampId() == 0 ? null : new ChannelTonBingCampBuff(city.getCampId(), city.getOccupyTime(), city.getCityId(), typeConfig.getBuffEffect(), time);
                newBuff = new ChannelTonBingCampBuff(campId, time, city.getCityId(), typeConfig.getBuffEffect());
            }

            // 先计算buff
            if (oldBuff != null) {
                beforeEndBuff(oldBuff);
            }
            if (newBuff != null) {
                beforeBeginBuff(newBuff);
            }

            //领取一下城池收货
            makeAllCityCollect(city.getCampId(), time);

            // 修改城池数据
            city.setCampId(campId);
            city.setCollectionTime(time);
            city.setOccupyTime(time);
            city.setDefTeamList(defList);
            city.setAtkTeamList(atkList);

            // 再计算buff
            if (oldBuff != null) {
                afterEndBuff(oldBuff);
            }
            if (newBuff != null) {
                afterBeginBuff(newBuff);
            }

            // 如果有进攻方，准继续战斗
            if (city.getAtkTeamList().size() > 0) {
                addTask(new ChannelTonBingBattleTask(this, calcNextBattleTime(city), city.getCityId()));
            }
            // 被占领后开始恢复额外守军
            city.setLastRecoveryExtraTeamTime(time);
            addTask(new ChannelTonBingRecoveryExtraTeamTask(this, getNextRecoveryExtraTeamTime(city), city.getCityId()));
        }

        // 城池守军恢复时间
        city.setTeamRecoveryBeginTime(time);
        addTask(new ChannelTonBingCityTeamRecoveryTask(this, config.getCityTeamRecoveryTime() + city.getTeamRecoveryBeginTime(), city.getCityId()));

        // 加聊天
        addSystemChat(eMessageType.TonBingPublicBattleChat, config.getActivityInfo().getActivityId(),
                (!defWin ? 1 : 2) + config.getTalkSeparator() + city.getCityId() + config.getTalkSeparator() + city.getCampId() + config.getTalkSeparator() + city.getBattledNum() + config.getTalkSeparator() + defCampId,
                time);

        // 同步顶部横幅
        syncTopMsg(city.getCampId(), city.getCityId(), defWin ? 2 : 1, battlefield, defCampId);
    }

    /**
     * 未战斗直接攻取城池
     */
    public void handleNoBattleOccupy(ChannelTonBingCity city, long time) {

        ChannelTonBingCityConfig cityConfig = config.getCityConfigMap(channelTonBingData.getWarType()).get(city.getCityId());
        ChannelTonBingCityTypeConfig typeConfig = config.getCityTypeConfigMap(channelTonBingData.getWarType()).get(cityConfig.getType());

        int atkCampId = getUserData(city.getAtkTeamList().getFirst().getUserId()).getCampId();
        int defCampId = city.getCampId();
        int winCampId = atkCampId;
        boolean defWin = false;

        // 【加城池战报】recordBattleReport

        ChannelTonBingCityWarReport report = new ChannelTonBingCityWarReport();
        report.setActivityId(config.getActivityInfo().getActivityId());
        report.setAtkCampId(Collections.singletonList(atkCampId));
        report.setDefCampId(defCampId);
        report.setType(defWin ? 2 : 1);
        report.setCityId(city.getCityId());
        report.setTime(time);
        report.setAtkRankList(new LinkedList<>());
        report.setDefRankList(new LinkedList<>());
        report.setWinCampId(winCampId);
        report.setMatchId(channelTonBingData.getMatchId());
        addCityWarReport(report);

        // 【处理战斗结束】handleBattleFinish

        // 同阵营队伍抽出来
        LinkedList<ChannelTonBingTeamArrive> atkList = new LinkedList<>();
        LinkedList<ChannelTonBingTeamArrive> defList = new LinkedList<>();
        for (ChannelTonBingTeamArrive arrive : new LinkedList<>(city.getAtkTeamList())) {
            if (winCampId == getUserData(arrive.getUserId()).getCampId()) {
                defList.add(arrive);
            } else {
                atkList.add(arrive);
            }
        }
        atkList.sort(Comparator.comparing(ChannelTonBingTeamArrive::getTime));
        defList.sort(Comparator.comparing(ChannelTonBingTeamArrive::getTime));

        // 拿buff
        ChannelTonBingCampBuff oldBuff = null;
        ChannelTonBingCampBuff newBuff = null;
        if (typeConfig.getBuffEffect() != 0) {
            oldBuff = city.getCampId() == 0 ? null : new ChannelTonBingCampBuff(city.getCampId(), city.getOccupyTime(), city.getCityId(), typeConfig.getBuffEffect(), time);
            newBuff = new ChannelTonBingCampBuff(winCampId, time, city.getCityId(), typeConfig.getBuffEffect());
        }

        // 先计算buff
        if (oldBuff != null) {
            beforeEndBuff(oldBuff);
        }
        if (newBuff != null) {
            beforeBeginBuff(newBuff);
        }

        //领取一下城池收货
        makeAllCityCollect(city.getCampId(), time);

        // 修改城池数据
        city.setCampId(winCampId);
        city.setCollectionTime(time);
        city.setOccupyTime(time);
        city.setDefTeamList(defList);
        city.setAtkTeamList(atkList);

        // 再计算buff
        if (oldBuff != null) {
            afterEndBuff(oldBuff);
        }
        if (newBuff != null) {
            afterBeginBuff(newBuff);
        }

        // 如果有进攻方，准继续战斗
        if (city.getAtkTeamList().size() > 0) {
            addTask(new ChannelTonBingBattleTask(this, calcNextBattleTime(city), city.getCityId()));
        }

        // 城池守军恢复时间
        city.setTeamRecoveryBeginTime(time);

        addTask(new ChannelTonBingCityTeamRecoveryTask(this, config.getCityTeamRecoveryTime() + city.getTeamRecoveryBeginTime(), city.getCityId()));

        // 被占领后开始恢复额外守军
        city.setLastRecoveryExtraTeamTime(time);
        addTask(new ChannelTonBingRecoveryExtraTeamTask(this, getNextRecoveryExtraTeamTime(city), city.getCityId()));

        // 加聊天
        addSystemChat(eMessageType.TonBingPublicBattleChat, config.getActivityInfo().getActivityId(),
                (!defWin ? 1 : 2) + config.getTalkSeparator() + city.getCityId() + config.getTalkSeparator() + city.getCampId() + config.getTalkSeparator() + 0 + config.getTalkSeparator() + defCampId,
                time);

        // 同步顶部横幅
        syncTopMsg(city.getCampId(), city.getCityId(), defWin ? 2 : 1, null, defCampId);
    }

    /**
     * 移除空战场
     *
     * @return 清空战场的ID
     */
    public List<Integer> removeEmptyBattlefield(ChannelTonBingCity city) {
        List<Integer> logRemove = new ArrayList<>();
        ChannelTonBingBattlefield battlefield = city.getBattlefield();
        Map<Integer, ChannelTonBingBattlefieldPart> partMap = battlefield.getPartMap();
        for (Map.Entry<Integer, ChannelTonBingBattlefieldPart> entry : partMap.entrySet()) {
            ChannelTonBingBattlefieldPart part = entry.getValue();
            LinkedList<ChannelTonBingBattlefieldTeam> atkList = part.getAtkList();
            LinkedList<ChannelTonBingBattlefieldTeam> defList = part.getDefList();
            if (atkList.isEmpty() || defList.isEmpty()) {
                // 队伍回到排队里
                teamReturnWaitList(city, part);
                // 移除空战场
                partMap.remove(entry.getKey());
                // 记录ID
                logRemove.add(part.getPartId());
            }
        }
        return logRemove;
    }

    private int getBattleField(ChannelTonBingUserData userData,long userId, ChannelTonBingProto.CTonBingGetBattlefieldReqMsg reqMsg) {
        int cityId = reqMsg.getCityId();
        ChannelTonBingCity city = getChannelTonBingData().getCityMap().get(cityId);
        if (city == null) {
            return GameErrorCode.E_TON_BING_PARAM_ERR;
        }
        ChannelTonBingBattlefield battlefield = city.getBattlefield();
        if (battlefield == null) {
            return GameErrorCode.E_TON_BING_NO_BATTLE;
        }
        ChannelTonBingBattlefieldPart part = null;
        if (reqMsg.hasPartId()) {
            part = battlefield.getPartMap().get(reqMsg.getPartId());
        } else {
            for (ChannelTonBingBattlefieldPart tmp : battlefield.getPartMap().values()) {
                part = tmp;
                break;
            }
        }
        if (part == null) {
            return GameErrorCode.E_TON_BING_NO_BATTLE;
        }

        ChannelTonBingProto.CTonBingGetBattlefieldRespMsg.Builder builder = ChannelTonBingProto.CTonBingGetBattlefieldRespMsg.newBuilder();
        builder.setRet(0);
        builder.setBattlefield(parseTonBingBattlefieldMsg(part, battlefield, city));

        // 进入场景
        ScenePlayerInfo scenePlayerInfo = new ScenePlayerInfo(userData.getServerId(), userId, "", "");
        UserBaseInfo userBaseInfo = ChannelUserMgr.getUserBaseInfo(userId);
        if (userBaseInfo != null) {
            scenePlayerInfo = new ScenePlayerInfo(userBaseInfo.getServerId(), userId, userBaseInfo.getNickName(), userBaseInfo.getDecoration());
        }
        UserScene userScene = new UserScene(
                config.getActivityInfo().getActivityId(),
                eSceneType.TonBingBattlefield.getValue(),
                channelTonBingData.getMatchId() + "-" + getBattlefieldSceneKey(cityId, part.getPartId()),
                userId,
                scenePlayerInfo,
                System.currentTimeMillis());
        ChannelSceneMgr.enterScene(userScene, false);

        sendChannelPacket(ClientProtocol.U_CTON_BING_GET_BATTLE_FIELD, builder, userData);
        return 0;
    }

    public int getUserChannelId(long userId){
        ChannelTonBingUserData data = getUserData(userId);
        if(data != null && StringUtils.isNullOrEmpty(data.getUnionUid())){
            ChannelTonBingUnionJoin unionData = getUnionJoin(data.getUnionUid());
            Map<Long, ChannelTonBingUnionJoinMember> mMap = unionData.getMemberMap();
            if(mMap.containsKey(userId)){
                return mMap.get(userId).getChannelId();
            }
        }
        return 0;
    }

    /**
     * 使用战鼓
     */
    public int useWarDrum(long userId, ChannelTonBingProto.CTonBingUseWarDrumReqMsg reqMsg) {
        int cityId = reqMsg.getCityId();

        ChannelTonBingCity city = getChannelTonBingData().getCityMap().get(cityId);
        if (city == null) {
            return GameErrorCode.E_TON_BING_CITY_NO_EXIST;
        }

        ChannelTonBingUserData userData = getUserData(userId);
        if (!isAble(userData)) {
            return GameErrorCode.E_TON_BING_NO_ABLE;
        }

        synchronized (lock) {

            // 是否战斗中
            ChannelTonBingBattlefield battlefield = city.getBattlefield();
            if (battlefield == null) {
                return GameErrorCode.E_TON_BING_NO_BATTLE;
            }

            // 拿道具效果
            /*GoodsInfo goodsInfo = GoodsMgr.getGoodsInfo(config.getZhanGuItem());
            if (goodsInfo == null) {
                return GameErrorCode.E_BAG_ITEM_NO_EXIST;
            }
            int effectTimes = goodsInfo.getParamList().get(0).intValue();*/
            int effectTimes = config.getZhanguParam1();

            // 有基础城防军无法使用战鼓
            if (getDefTeamNum(city, battlefield, eTonBingSoldierType.COMMON_SOLDIER.getValue()) > 0) {
                return GameErrorCode.E_TON_BING_HAS_DEF_TEAM;
            }
            if (getDefTeamNum(city, battlefield, eTonBingSoldierType.EXTRA_SOLDIER.getValue()) == 0) {
                for (ChannelTonBingBattlefieldPart part : battlefield.getPartMap().values()) {
                    for (ChannelTonBingBattlefieldTeam team : new LinkedList<>(part.getDefList())) {
                        if (team.getTeam() == null) {
                            return GameErrorCode.E_TON_BING_HAS_DEF_TEAM;
                        }
                    }
                }
            }


            // 看看有没有自己的阵营在里面
            int myCampId = userData.getCampId();
            boolean hasMyCamp = false;
            if (city.getCampId() == myCampId) {
                hasMyCamp = true;
            }
            if (!hasMyCamp) {
                for (ChannelTonBingTeamArrive arrive : new LinkedList<>(city.getAtkTeamList())) {
                    if (getUserData(arrive.getUserId()).getCampId() == myCampId) {
                        hasMyCamp = true;
                        break;
                    }
                }
            }
            if (!hasMyCamp) {
                for (ChannelTonBingBattlefieldPart part : battlefield.getPartMap().values()) {
                    for (ChannelTonBingBattlefieldTeam tempTeam : new LinkedList<>(part.getAtkList())) {
                        if (myCampId == getUserData(tempTeam.getTeam().getUserId()).getCampId()) {
                            hasMyCamp = true;
                            break;
                        }
                    }
                }
            }
            if (!hasMyCamp) {
                return GameErrorCode.E_TON_BING_NO_MY_CAMP_TEAM;
            }

            // 【统兵演武取消加速限制】
//            // 当战场的加速场次＞较高方部队数/战场数的时候,无需加速
//            int atkTeamCnt = 0;
//            int defTeamCnt = 0;
//            for (ChannelTonBingBattlefieldPart part : battlefield.getPartMap().values()) {
//                atkTeamCnt += part.getAtkList().size();
//                defTeamCnt += part.getDefList().size();
//            }
//            if (battlefield.getPartMap().size() != 0 && battlefield.getDrumTimes() > (Math.max(atkTeamCnt, defTeamCnt) / battlefield.getPartMap().size())) {
//                return GameErrorCode.E_TON_BING_USE_DRUM_LIMIT;
//            }

            // 战鼓生效
            if (battlefield.getDrumTimes() <= 0) {
                battlefield.setUseDrumTime(System.currentTimeMillis());
            }
            battlefield.setDrumTimes(battlefield.getDrumTimes() + effectTimes);

            // 同步所有人
            for (ChannelTonBingBattlefieldPart part : battlefield.getPartMap().values()) {
                syncUseWarDrum(city, battlefield, part);
            }
        }

        return 0;
    }

    /**
     * 获取城防军的数量
     */
    private int getSoldierTeamCount(ChannelTonBingCity city) {
        return city.getExtraTeamNum() + city.getTeamNum();
    }

    // ============================== 移动 ==============================

    /**
     * 强制进入城池
     */
    private void forceArriveNextCity(ChannelTonBingTeam team, long arriveTime) {
        synchronized (lock) {
            ChannelTonBingUserData userData = getUserData(team.getUserId());
            ArrayList<Integer> newRouteList = new ArrayList<>(team.getRouteList());
            if(newRouteList.size() == 0){
                return;
            }
            int cityId = newRouteList.remove(0);
            ChannelTonBingCity city = getChannelTonBingData().getCityMap().get(cityId);

            // 是否为自己主城
            boolean selfMainCity = false;
            ChannelTonBingCityConfig cityConfig = config.getCityConfigMap(channelTonBingData.getWarType()).get(cityId);
            if (cityConfig.getType() == 1 && cityConfig.getInitialCamp() == userData.getCampId()) {
                selfMainCity = true;
            }

            // 【进城池队列】
            if (!selfMainCity) { // 自己主城不需要进进攻防守
                ChannelTonBingTeamArrive arrive = new ChannelTonBingTeamArrive(team.getUserId(), team.getOccupation(), arriveTime);
                if (city.getCampId() == userData.getCampId()) {
                    // 进入防守队列
                    city.getDefTeamList().addLast(arrive);
                } else {
                    // 进入进攻队列
                    city.getAtkTeamList().addLast(arrive);
                }
            }

            // 【开战】
            boolean haveCreateBattlefield = false;
            if (city.getBattlefield() == null && city.getAtkTeamList().size() > 0 && city.getDefTeamList().isEmpty() && getSoldierTeamCount(city) == 0) {
                // 对方没人，直接拿下
                handleNoBattleOccupy(city, arriveTime);
            } else if (city.getBattlefield() == null && city.getAtkTeamList().size() > 0) {
                // 开新战场
                createBattlefield(city);
                haveCreateBattlefield = true;
            } else if (city.getBattlefield() != null) {
                // 队伍加入战场
                teamToBattlefield(city, true);
            }

            boolean syncSelfToAll = (!newRouteList.isEmpty() && city.getBattlefield() != null);

            // 【修改队伍状态】
            team.setCityId(cityId);
            if (newRouteList.isEmpty() || city.getBattlefield() != null) {
                team.setBeginMoveTime(0);
                team.setRouteList(new ArrayList<>());
                team.setSmallMoveTime(0);
                team.setArriveTime(0);
                // 同步城池
                addNeedSyncCity(cityId);

                // 日志
                ChannelAutoLogMgr.add(new LogChannelTonBingTeamMove(
                        config.getActivityInfo().getActivityId(),
                        team.getUserId(),
                        team.getOccupation(),
                        team.getCityId(),
                        team.getRouteList(),
                        channelTonBingData.getMatchId(),
                        channelTonBingData.getTurn(),
                        channelTonBingData.getWarType()
                ));
            } else {
                moveByRoute(team, newRouteList, true);
            }

            // 同步队伍
            if (haveCreateBattlefield) {
                syncCityTeamToSelf(city); // 城池开战，所有玩家改状态
            }
            if (syncSelfToAll) {
                addNeedSyncTeam(team); // 移动被打断，同步所有玩家
            }
            syncTeamToSelf(team.getUserId(), Collections.singletonList(team)); // 自己队伍要一直同步

            userData.setUpdateOption();
            channelTonBingData.setUpdateOption();
        }
    }

    /**
     * 计算下次到达时间
     */
    public long calcMoveTime(int campId, List<Integer> routeList) {
        // 最后一个城池，不是自己的，用慢速的时间，其余的用快速的
        if (routeList.size() == 1) {
            Integer lastCityId = routeList.get(0);
            ChannelTonBingCity city = getChannelTonBingData().getCityMap().get(lastCityId);
            if (city != null && city.getCampId() != campId) {
                return config.getMoveTime();
            }
        }
        return config.getMoveTimeFast();
    }

    /**
     * 出发
     * （不会同步队伍）
     */
    public void moveByRoute(ChannelTonBingTeam team, List<Integer> routeList, boolean continueMove) {
        // 队伍从旧城池移除
        int cityId = team.getCityId();
        ChannelTonBingCity city = getChannelTonBingData().getCityMap().get(cityId);
        ChannelTonBingTeamArrive arrive = null;
        for (ChannelTonBingTeamArrive tmp : new LinkedList<>(city.getDefTeamList())) {
            if (tmp.getUserId() == team.getUserId() && tmp.getTeamId() == team.getOccupation()) {
                arrive = tmp;
                break;
            }
        }
        if (arrive != null) {
            city.getDefTeamList().remove(arrive);
        }

        // 出发
        if (!continueMove) {
            long now = System.currentTimeMillis();
            team.setBeginMoveTime(now);
            team.setRouteList(routeList);
            team.setSmallMoveTime(now);
            team.setArriveTime(now + calcMoveTime(getUserData(team.getUserId()).getCampId(), routeList));
        } else {
            team.setRouteList(routeList);
            team.setSmallMoveTime(team.getArriveTime());
            team.setArriveTime(team.getArriveTime() + calcMoveTime(getUserData(team.getUserId()).getCampId(), routeList));
        }
        getUserData(team.getUserId()).setUpdateOption();
        addTask(new ChannelTonBingMoveTask(this, team.getArriveTime(), team.getUserId(), team.getOccupation()));
    }

    // ============================== 地图 ==============================

    private Map<Integer, ChannelTonBingCity> createCity() {

        Map<Integer, ChannelTonBingCity> cityMap = new ConcurrentHashMap<>();

        for (ChannelTonBingCityConfig cityConfig : config.getCityConfigMap(channelTonBingData.getWarType()).values()) {
            ChannelTonBingCityTypeConfig typeConfig = config.getCityTypeConfigMap(channelTonBingData.getWarType()).get(cityConfig.getType());

            ChannelTonBingCity city = new ChannelTonBingCity();
            city.setCityId(cityConfig.getId());
            city.setCampId(cityConfig.getInitialCamp());
            city.setTeamNum(typeConfig.getTeamNumTurn(channelTonBingData.getTurn()));
            city.setTeamSoldierNum(typeConfig.getSoldierNumTurn(channelTonBingData.getTurn()));
            Long startTime = config.getStage1TimeList().get(channelTonBingData.getTurn() - 1).get(0);
            city.setCollectionTime(startTime);
            city.setLastRecoveryExtraTeamTime(startTime);
            cityMap.put(cityConfig.getId(), city);
        }

        return cityMap;
    }

    private Map<Integer, ChannelTonBingCityEvent> createInitCityEvent() {
        List<Integer> cityIdList = config.getCityEventInitConfigList();
        List<Integer> configList = config.getCityEventConfigList();

        Map<Integer, ChannelTonBingCityEvent> map = new ConcurrentHashMap<>();

        // 首次刷新时间 = 活动开始第一天的第一个刷新时间
        long zeroTime = DateHelper.getZeroTimeStamp(config.getStage1TimeList().get(channelTonBingData.getTurn() - 1).get(0));
        long cityEventTime = getNextCityEventTime(zeroTime, true);

        for (int i = 0; i < cityIdList.size(); i++) {
            int campKey = i + 1;
            int buffId = configList.get(randomHelper.next(configList.size()));
            int cityId = cityIdList.get(i);

            ChannelTonBingCityEvent event = new ChannelTonBingCityEvent(
                    campKey,
                    cityId,
                    buffId,
                    cityEventTime
            );

            map.put(event.getCampKey(), event);
        }

        return map;
    }

    private int getBirthCityId(int campId) {
        for (ChannelTonBingCityConfig cityConfig : config.getCityConfigMap(channelTonBingData.getWarType()).values()) {
            if (cityConfig.getType() == 1 && cityConfig.getInitialCamp() == campId) {
                return cityConfig.getId();
            }
        }
        return 0;
    }

    /**
     * 前往城池
     */
    private int moveToCity(long userId, ChannelTonBingProto.CTonBingMoveTeamReqMsg reqMsg) {
        ChannelTonBingUserData userData = getUserData(userId);
        List<Integer> occupationList = reqMsg.getOccupationList();
        int toCityId = reqMsg.getToCityId();
        boolean isTargetCity = reqMsg.getIsTargetCity();

        // 时间判断
        eTonBingStage stage = this.config.getTonBingStage();
        if (stage != eTonBingStage.Stage1Time) {
            return GameErrorCode.E_TON_BING_NO_OPERATE_TIME;
        }

        // 参数校验
        if (!isAble(userData)) {
            return GameErrorCode.E_TON_BING_NO_ABLE;
        }
        for (Integer occu : occupationList) {
            if (!userData.getTeamMap().containsKey(occu)) {
                return GameErrorCode.E_TON_BING_TEAM_NO_EXIST;
            }
        }

        synchronized (lock) {

            // 遍历所有队伍
            for (Integer occu : occupationList) {
                ChannelTonBingTeam team = userData.getTeamMap().get(occu);

                // 跳过已经在这里的
                if (team.getCityId() == toCityId) {
                    continue;
                }
                // 前往城池
                int ret = moveToCity(team, toCityId);
                if (ret != 0) {
                    return ret;
                }
                if (isTargetCity) {
                    team.setTargetCityId(toCityId);
                }
            }
        }
        return 0;
    }

    /**
     * 前往城池
     */
    private int moveToCity(ChannelTonBingTeam team, int toCityId) {
        ChannelTonBingCityConfig toCityConfig = null;
        Map<Integer, ChannelTonBingCityConfig> cityConfigMap = config.getCityConfigMap(channelTonBingData.getWarType());
        if(cityConfigMap != null){
            toCityConfig = cityConfigMap.get(toCityId);
        }
        if (toCityConfig == null) {
            return GameErrorCode.E_TON_BING_CITY_NO_EXIST;
        }
        // 时间判断
        eTonBingStage stage = this.config.getTonBingStage();
        if (stage != eTonBingStage.Stage1Time) {
            return GameErrorCode.E_TON_BING_NO_OPERATE_TIME;
        }

        // 状态判断
        if (getTeamStatus(team) != eTonBingTeamStatus.Free) {
            return GameErrorCode.E_TON_BING_TEAM_CHANGE_SOLDIER_NO_FREE;
        }

        // 判断是否达到兵力最低要求,不满足的跳过处理
        // 千分比最低兵力和固定最低兵力取最大值
        long minSoldierNum = BigDecimal.valueOf(team.getMaxSoldier())
                .multiply(BigDecimal.valueOf(config.getTeamNumLowerLimitParam()))
                .divide(BigDecimal.valueOf(1000), 0, BigDecimal.ROUND_UP).longValue();
        if (team.getSoldierNum() < Math.max(minSoldierNum, config.getTeamNumLowerLimitFixedParam())) {
            return GameErrorCode.E_TON_BING_TEAM_SOLDIER_MIN_LIMIT;
        }
        // 找路径
        List<Integer> cityRouteList = getRouteList(team, toCityId);

        if (cityRouteList == null || cityRouteList.size() <= 1) {
            return GameErrorCode.E_TON_BING_NO_ROUTE;
        }
        // 出发点不要
        cityRouteList.remove(0);

        // 移动
        moveByRoute(team, cityRouteList, false);
        addNeedSyncTeam(team);

        // 日志
        ChannelAutoLogMgr.add(new LogChannelTonBingTeamMove(
                config.getActivityInfo().getActivityId(),
                team.getUserId(),
                team.getOccupation(),
                team.getCityId(),
                team.getRouteList(),
                channelTonBingData.getMatchId(),
                channelTonBingData.getTurn(),
                channelTonBingData.getWarType()
        ));
        return 0;
    }

    /**
     * 获取当前队伍到指定城池的路径
     * @param team
     * @param toCityId
     */
    private List<Integer> getRouteList(ChannelTonBingTeam team, int toCityId) {
        ChannelTonBingCityConfig toCityConfig = config.getCityConfigMap(channelTonBingData.getWarType()).get(toCityId);
        if (toCityConfig == null) {
            return null;
        }
        ChannelTonBingUserData userData = getUserData(team.getUserId());
        ChannelTonBingCityConfig curCityConfig = config.getCityConfigMap(channelTonBingData.getWarType()).get(team.getCityId());
        // 能走的格子，未处于战斗状态
        Map<Integer, List<Integer>> canMoveCityMap = new ConcurrentHashMap<>();
        // 能走的格子，处于战斗状态
        Map<Integer, List<Integer>> battlefieldRouteMap = new ConcurrentHashMap<>();
        for (ChannelTonBingCity city : channelTonBingData.getCityMap().values()) {
            // 没有在战斗的己方城池
            if (city.getCampId() == userData.getCampId()) {
                ChannelTonBingCityConfig safeCityConfig = config.getCityConfigMap(channelTonBingData.getWarType()).get(city.getCityId());
                if (city.getBattlefield() == null) {
                    canMoveCityMap.put(safeCityConfig.getId(), config.getCityConnectMap().get(safeCityConfig.getId()));
                } else {
                    battlefieldRouteMap.put(safeCityConfig.getId(), config.getCityConnectMap().get(safeCityConfig.getId()));
                }
            }
        }

        // 判断是否移除中心城
        int nowTurn = config.getTurn() + 1;
        Map<Integer, Long> stage2BeginTimeMap = config.getStage2BeginTimeMap();
        if(stage2BeginTimeMap.containsKey(nowTurn)){
            if (System.currentTimeMillis() < stage2BeginTimeMap.get(nowTurn)) {
                for (ChannelTonBingCityConfig config : config.getCityConfigMap(channelTonBingData.getWarType()).values()) {
                    if (config.getType() == 6) {
                        canMoveCityMap.remove(config.getId());
                        if (toCityId == config.getId()) {
                            return null;
                        }
                    }
                }
            }
        }


        // 起点强制加进去
        canMoveCityMap.put(curCityConfig.getId(), config.getCityConnectMap().get(curCityConfig.getId()));
        return TonBingMapLogic.getRouteList(canMoveCityMap, battlefieldRouteMap, curCityConfig.getId(), toCityConfig.getId());
    }

    private int speedUp(long userId, int occupation, int times) {
        // 参数校验
        ChannelTonBingUserData userData = getUserData(userId);
        if (!isAble(userData)) {
            return GameErrorCode.E_TON_BING_NO_ABLE;
        }
        ChannelTonBingTeam team = userData.getTeamMap().get(occupation);
        if (team == null) {
            return GameErrorCode.E_TON_BING_TEAM_NO_EXIST;
        }

        // 时间判断
        eTonBingStage stage = this.config.getTonBingStage();
        if (stage != eTonBingStage.Stage1Time) {
            return GameErrorCode.E_TON_BING_NO_OPERATE_TIME;
        }

        // 取道具效果
        GoodsInfo goodsInfo = GoodsMgr.getGoodsInfo(config.getShenXingItem());
        if (goodsInfo == null) {
            return GameErrorCode.E_GOODS_NO_EXIST;
        }
        long reduceTime = goodsInfo.getParamList().get(0).longValue() * DateHelper.SECOND_MILLIONS;
        long totalReduceTime = times * reduceTime;
        long realReduceTime = 0L;
        long now = System.currentTimeMillis();

        synchronized (lock) {
            while (getTeamStatus(team) == eTonBingTeamStatus.Move) {
                if (realReduceTime + team.getArriveTime() - now < totalReduceTime) {
                    // 可以到达
                    realReduceTime = realReduceTime + team.getArriveTime() - now;
                    team.setArriveTime(now);
                    forceArriveNextCity(team, team.getArriveTime());
                } else {
                    // 无法到达
                    team.setArriveTime(team.getArriveTime() - (totalReduceTime - realReduceTime));
                    realReduceTime = totalReduceTime;
                    // 到达任务加回去
                    addTask(new ChannelTonBingMoveTask(this, team.getArriveTime(), userId, occupation));
                    break;
                }
            }
        }

        // 真实消耗道具数量
        int realTimes = BigDecimal.valueOf(realReduceTime).divide(BigDecimal.valueOf(reduceTime), 0, BigDecimal.ROUND_UP).intValue();

        // 返回
        ChannelTonBingProto.CTonBingMoveSpeedUpRespMsg.Builder resp = ChannelTonBingProto.CTonBingMoveSpeedUpRespMsg.newBuilder();
        resp.setRet(0);
        resp.setTeam(parseTonBingTeamMsg(userData, team));

        ChannelTonBingProto.ChannelTonBingMoveSpeedUpRespMsg.Builder builder = ChannelTonBingProto.ChannelTonBingMoveSpeedUpRespMsg.newBuilder();
        builder.setResp(resp);
        builder.setReqTimes(times);
        builder.setRealTimes(realTimes);
        sendChannelPacket(GameProtocol.S_CTON_BING_MOVE_SPEED_UP_FROM_CHANNEL, builder, userData);
        return 0;
    }

    private List<Integer> getMiddleCityId(int campId1, int campId2) {
        ChannelTonBingData data = getChannelTonBingData();

        Set<Integer> cityList1 = new HashSet<>();
        Set<Integer> cityList2 = new HashSet<>();
        for (ChannelTonBingCity city : data.getCityMap().values()) {
            if (city.getCampId() == campId1) {
                cityList1.add(city.getCityId());
            } else if (city.getCampId() == campId2) {
                cityList2.add(city.getCityId());
            }
        }

        Set<Integer> middleCityId = new HashSet<>(TonBingMapLogic.getMiddleCityId(config.getCityConnectMap(), cityList1, cityList2));

        return new ArrayList<>(middleCityId);
    }

    private int getCityTeam(long userId, ChannelTonBingProto.CTonBingSyncCityTeamReqMsg reqMsg) {
        // 解析参数
        Map<Integer, Long> cityTimeMap = new ConcurrentHashMap<>();
        for (ChannelTonBingProto.CTonBingSyncCityTeamMsg cityTeamMsg : reqMsg.getCityInfoList()) {
            cityTimeMap.put(cityTeamMsg.getCityId(), cityTeamMsg.getTime());
        }

        long now = System.currentTimeMillis();

        // 遍历全部玩家，把城池内移动中的队伍加进来
        Map<Integer, LinkedList<ChannelTonBingShowTeam>> campTeamMap = new HashMap<>();
        for (ChannelTonBingUserData userData : userDataMap.values()) {
            if (!isAble(userData)) {
                continue; // 跳过：无资格
            }

            campTeamMap.putIfAbsent(userData.getCampId(), new LinkedList<>());

            for (ChannelTonBingTeam team : userData.getTeamMap().values()) {
                if (!cityTimeMap.containsKey(team.getCityId())) {
                    continue; // 跳过：不在该城市的队伍
                }

                if (getTeamStatus(team) == eTonBingTeamStatus.Move) {
                    campTeamMap.get(userData.getCampId()).add(new ChannelTonBingShowTeam(userData, team));
                }
            }
        }

        // 排序
        ChannelTonBingUserData myUserData = getUserData(userId);
        int myCampId = myUserData.getCampId();
        String myUnionUid = myUserData.getUnionUid();
        for (LinkedList<ChannelTonBingShowTeam> teamLinkedList : campTeamMap.values()) {
            teamLinkedList.sort(new Comparator<ChannelTonBingShowTeam>() {
                @Override
                public int compare(ChannelTonBingShowTeam o1, ChannelTonBingShowTeam o2) {
                    ChannelTonBingUserData userData1 = o1.getUserData();
                    ChannelTonBingUserData userData2 = o2.getUserData();
                    // 【同阵营比较】
                    if (userData1.getCampId() == myCampId) {
                        // 自己的队伍优先
                        if (userData1.getUserId() == userId || userData2.getUserId() == userId) {
                            if (userData1.getUserId() != userData2.getUserId()) {
                                return userData1.getUserId() == userId ? 1 : -1;
                            }
                        }
                        // 同商会优先
                        boolean unionEq1 = Objects.equals(userData1.getUnionUid(), myUnionUid);
                        boolean unionEq2 = Objects.equals(userData2.getUnionUid(), myUnionUid);
                        if (unionEq1 || unionEq2) {
                            if (unionEq1 != unionEq2) {
                                return unionEq1 ? 1 : -1;
                            }
                        }
                    }
                    // 【通用比较】
                    // 战功优先
                    if (userData1.getZhanGong() != userData2.getZhanGong()) {
                        return userData1.getZhanGong() > userData2.getZhanGong() ? 1 : -1;
                    }
                    return 0;
                }
            }.reversed());
        }

        // 找需要发给客户端的
        int campTeamNum = config.getShowCampTopNum();
        List<ChannelTonBingTeam> teamList = new ArrayList<>();
        for (LinkedList<ChannelTonBingShowTeam> linkedList : campTeamMap.values()) {
            int num = 0;
            for (ChannelTonBingShowTeam showTeam : linkedList) {
                if (num++ >= campTeamNum) {
                    break;
                }
                ChannelTonBingTeam team = showTeam.getTeam();
                Long lReqTime = cityTimeMap.get(team.getCityId());
                if(null == lReqTime){
                    continue;
                }
                long updateMoveTime = team.getMoveUpdateTime();
                if (updateMoveTime - lReqTime > DateHelper.SECOND_MILLIONS * 60) {
                    teamList.add(team);
                }
            }
        }

        // 返回
        ChannelTonBingProto.CTonBingSyncCityTeamRespMsg.Builder builder = ChannelTonBingProto.CTonBingSyncCityTeamRespMsg.newBuilder();
        for (ChannelTonBingProto.CTonBingSyncCityTeamMsg cityMsg : reqMsg.getCityInfoList()) {
            builder.addCityInfo(cityMsg.toBuilder().setTime(now));
        }
        for (ChannelTonBingTeam team : teamList) {
            builder.addTeam(parseTonBingTeamMsg(getUserData(team.getUserId()), team));
        }
        ChannelTonBingUserData userData = getUserData(userId);
        sendChannelPacket(ClientProtocol.U_CTON_BING_SYNC_CITY_TEAM, builder, userData);
        return 0;
    }


    // ============================== 报名 ==============================
    public void initData(List<ChannelTonBingCityWarReport> tempCityWarReportList,
                         List<ChannelTonBingUserWarReport> tempUserWarReportsList,
                         Map<Integer, ChannelTonBingCampMatchData> tempCampDataMap,
                         Map<String, ChannelTonBingUnionJoin> tempJoinMap,
                         Map<Long, ChannelTonBingUserData> tempUserDataMap){
        cityWarReportList = tempCityWarReportList;
        userWarReportsList = tempUserWarReportsList;
        campMatchDataMap = tempCampDataMap;
        unionJoinMap = tempJoinMap;
        userDataMap = tempUserDataMap;
    }
    /**
     * 商会怼进来开始bug比赛
     */
    public void initData() {
        synchronized (lock) {
            if (getChannelTonBingData().isLockUnion()) {
                return;
            }
            List<String> unionIdList = new ArrayList<>();
            // 缓存一下门客赚钱
            for (ChannelTonBingUnionJoin join : unionJoinMap.values()) {
                unionIdList.add(join.getUnionUid());
            }
            channelTonBingData.setUnionIdList(unionIdList);
            // 分阵营,不分了，生成阵营数据就刑
            lockCamp(new ArrayList<>(unionJoinMap.values()));

            // 改状态
            getChannelTonBingData().setLockUnion(true);
            logger.error("tonbing match {} lock union.matchId{}", config.getActivityInfo().getActivityId(),channelTonBingData.getMatchId());

            // 移动玩家到起始点
            initBirthCityId();
            channelTonBingInitTeamPosTask();

            // 初始化榜单，让0值的也上榜，才能看到自己阵营都有谁
            initAllRankList();

            // 同步状态
            for (ChannelTonBingUserData userData : userDataMap.values()) {
                syncBaseDataMsg(userData);
                // 跨服同步用户数据到游戏服
                syncUserDataToGameServer(userData);
            }
        }
    }

    private void initBirthCityId(){
        for(ChannelTonBingUserData userData : userDataMap.values()){
            Map<Integer, ChannelTonBingTeam> teamMap = userData.getTeamMap();
            for(ChannelTonBingTeam team : teamMap.values()){
                team.setCityId(0);
            }
            userData.setTeamMap(teamMap);
        }
    }

    /**
     * 锁定阵营
     */
    private void lockCamp(List<ChannelTonBingUnionJoin> unionJoinList) {
        synchronized (lock) {
            if (getChannelTonBingData().isLockUnion()) {
                return;
            }
            // 阵营数据生成
            initCampUserData();
        }
    }

    /**
     * 是否有资格
     */
    private boolean isAble(ChannelTonBingUserData userData) {
        if(userData == null){
            return false;
        }
        return userData.getCampId() != 0;
    }

    /**
     * 是否有资格
     */
    private boolean isAble(ChannelTonBingUnionJoin join) {
        return join.getCampId() != 0;
    }

    // ============================== 红点 ==============================

    /**
     * 添加红点
     */
    private void addRedDot(ChannelTonBingUserData userData, eTonBingRedDotType type) {
        if (userData.getRedDotMap().containsKey(type.getValue())) {
            return;
        }
        userData.getRedDotMap().put(type.getValue(), System.currentTimeMillis());
        userData.setUpdateOption();

        ChannelTonBingProto.CTonBingSyncRedDotRespMsg.Builder builder = ChannelTonBingProto.CTonBingSyncRedDotRespMsg.newBuilder();
        builder.addType(type.getValue());
        sendChannelPacket(ClientProtocol.U_CTON_BING_SYNC_RED_DOT, builder, userData);
    }

    // ============================== buff ==============================

    /**
     * 获取buff效果
     *
     * @return 看配置下标是哪个就拿哪个
     */
    private List<Integer> getBuffAddition(long userId, eTonBingBuffType buffType) {
        return getBuffAddition(getUserData(userId).getCampId(), buffType);
    }


    /**
     * 获取buff效果
     *
     * @return 看配置下标是哪个就拿哪个
     */
    private List<Integer> getBuffAddition(int campId, eTonBingBuffType buffType) {
        // 目前最多一个buff有2个效果
        List<Integer> resultList = new ArrayList<>();
        resultList.add(0);
        resultList.add(0);

        // 判断阵营
        if (campId == 0) {
            return resultList;
        }

        // 这几种buff从城池事件那边找
        if (buffType == eTonBingBuffType.Type1_TeamSoldierMaxNum
                || buffType == eTonBingBuffType.Type2_RecruitSoldierPrice
                || buffType == eTonBingBuffType.Type3_CityTeamMaxNum
                || buffType == eTonBingBuffType.Type4_Hospital) {
            List<ChannelTonBingCampBuff> list = getChannelTonBingData().getCampBuffMap().get(campId);
            if (list != null) {
                for (ChannelTonBingCampBuff campBuff : new ArrayList<>(list)) {
                    ChannelTonBingBuffTypeConfig buffTypeConfig = config.getBuffTypeConfigMap().get(campBuff.getBuffId());
                    if (buffTypeConfig != null) {
                        if (buffTypeConfig.getType() == buffType.getValue()) {
                            // buff加到结果中
                            List<Integer> effectList = StringUtils.stringToIntegerList(buffTypeConfig.getBuffEffect(), ";");
                            for (int i = 0; i < effectList.size(); i++) {

                                if (buffType == eTonBingBuffType.Type2_RecruitSoldierPrice && i == 0) {
                                    // 不正常乘算
                                    resultList.set(i, calcBuffType2_0(resultList.get(i), effectList.get(i)));
                                } else {
                                    // 正常加算
                                    resultList.set(i, resultList.get(i) + effectList.get(i));
                                }

                            }
                        }
                    }
                }
            }
            return resultList;
        }

        // 这几种buff从占领的城池找
        if (buffType == eTonBingBuffType.Type5_CityFoodProduce ||
                buffType == eTonBingBuffType.Type6_ReadySoldierMaxNum) {
            for (ChannelTonBingCity city : getChannelTonBingData().getCityMap().values()) {
                if (city.getCampId() == campId) {
                    ChannelTonBingCityConfig cityConfig = config.getCityConfigMap(channelTonBingData.getWarType()).get(city.getCityId());
                    ChannelTonBingCityTypeConfig cityTypeConfig = config.getCityTypeConfigMap(channelTonBingData.getWarType()).get(cityConfig.getType());
                    if (cityTypeConfig.getBuffEffect() != 0) {
                        ChannelTonBingBuffTypeConfig buffTypeConfig = config.getBuffTypeConfigMap().get(cityTypeConfig.getBuffEffect());
                        if (buffTypeConfig.getType() == buffType.getValue()) {
                            // buff加到结果中
                            List<Integer> effectList = StringUtils.stringToIntegerList(buffTypeConfig.getBuffEffect(), ";");
                            for (int i = 0; i < effectList.size(); i++) {
                                resultList.set(i, resultList.get(i) + effectList.get(i));
                            }
                        }
                    }
                }
            }
        }
        //排行buff
        if(buffType == eTonBingBuffType.Type7_CityFoodProduce){
            ChannelTonBingBuffTypeConfig rankBuffConfig = getRankBuffInfo(campId);
            if(rankBuffConfig != null){
                resultList.set(0,resultList.get(0) + Integer.valueOf(rankBuffConfig.getBuffEffect()));
            }
        }

        return resultList;
    }

    /**
     * 计算招兵减银两buff叠加效果
     * （减数值、多个buff叠加乘算）
     *
     * @param value1 之前BUFF全部叠加的减少值，千分比
     * @param value2 当前BUFF减少值，千分比
     */
    private int calcBuffType2_0(int value1, int value2) {

        // 先算实际消耗倍是原来多少倍，再用1减一下，就是buff减少的倍数
        // 1 - (1-效果1)(1-效果2)  =  效果1 + 效果2 - 效果1*效果2

        BigDecimal a1 = BigDecimal.valueOf(value1).divide(BigDecimal.valueOf(1000), 8, BigDecimal.ROUND_UP);
        BigDecimal a2 = BigDecimal.valueOf(value2).divide(BigDecimal.valueOf(1000), 8, BigDecimal.ROUND_UP);
        BigDecimal result = a1.add(a2).subtract(a1.multiply(a2));

        // 转千分比再返回
        return result.multiply(BigDecimal.valueOf(1000)).setScale(0, BigDecimal.ROUND_DOWN).intValue();
    }

    private List<Integer> getBuffAdditionParam(ChannelTonBingCampBuff buff) {
        ChannelTonBingBuffTypeConfig buffTypeConfig = config.getBuffTypeConfigMap().get(buff.getBuffId());
        return StringUtils.stringToIntegerList(buffTypeConfig.getBuffEffect(), ";");
    }

    private long getNextCityEventTime(long lastCityEventTime, boolean isRefresh) {
        List<Long> configList = isRefresh ? config.getCityEventRefreshTime() : config.getCityEventApplyTime();

        long zeroTimeStamp = DateHelper.getZeroTimeStamp(lastCityEventTime);
        if (lastCityEventTime >= zeroTimeStamp + configList.get(configList.size() - 1)) {
            zeroTimeStamp += DateHelper.DAY_MILLIONS; // 那天全部刷新完，就刷新下一天的
        }

        long resultTime = Long.MAX_VALUE;
        for (Long configTime : configList) {
            long tmpTime = zeroTimeStamp + configTime;
            if (tmpTime > lastCityEventTime) {
                resultTime = tmpTime;
                break;
            }
        }

        return resultTime;
    }

    private long getBuffEndTime(ChannelTonBingCampBuff buff) {
        ChannelTonBingBuffTypeConfig buffTypeConfig = config.getBuffTypeConfigMap().get(buff.getBuffId());
        if (buffTypeConfig.getContinueTime() == 0) {
            if (buff.getEndTime() != 0) {
                return buff.getEndTime(); // 城池易主结束时间
            } else {
                return config.getActivityInfo().getEndTime() * 1000; // 正常不会获取到这个
            }
        }
        return buff.getBeginTime() + buffTypeConfig.getContinueTime();
    }

    private void beforeEndBuff(ChannelTonBingCampBuff buff) {
        int campId = buff.getCampId();
        long buffEndTime = getBuffEndTime(buff);
        ChannelTonBingBuffTypeConfig buffTypeConfig = config.getBuffTypeConfigMap().get(buff.getBuffId());
        if (buffTypeConfig.getType() == eTonBingBuffType.Type1_TeamSoldierMaxNum.getValue()) {

        } else if (buffTypeConfig.getType() == eTonBingBuffType.Type2_RecruitSoldierPrice.getValue()) {

        } else if (buffTypeConfig.getType() == eTonBingBuffType.Type3_CityTeamMaxNum.getValue()) {

        } else if (buffTypeConfig.getType() == eTonBingBuffType.Type4_Hospital.getValue()) {
            for (ChannelTonBingUserData userData : userDataMap.values()) {
                if (userData.getCampId() == campId) {
                    recalculateBarracksHospitalRecovery(userData.getUserId());
                }
            }
        } else if (buffTypeConfig.getType() == eTonBingBuffType.Type5_CityFoodProduce.getValue()) {
            for (ChannelTonBingUserData userData : userDataMap.values()) {
                if (userData.getCampId() == campId) {
                    recalculateCityFoodProdProduction(buff.getCampId(), buffEndTime);
                }
            }
        } else if (buffTypeConfig.getType() == eTonBingBuffType.Type6_ReadySoldierMaxNum.getValue()) {

        }
    }

    private void afterEndBuff(ChannelTonBingCampBuff buff) {
        int campId = buff.getCampId();
        ChannelTonBingBuffTypeConfig buffTypeConfig = config.getBuffTypeConfigMap().get(buff.getBuffId());
        if (buffTypeConfig.getType() == eTonBingBuffType.Type1_TeamSoldierMaxNum.getValue()) {
            for (ChannelTonBingUserData userData : userDataMap.values()) {
                if (userData.getCampId() == campId) {
                    refreshTeam(userData);
                }
            }
        } else if (buffTypeConfig.getType() == eTonBingBuffType.Type2_RecruitSoldierPrice.getValue()) {

        } else if (buffTypeConfig.getType() == eTonBingBuffType.Type3_CityTeamMaxNum.getValue()) {
            synchronized (lock) {
                int addTeamNum = getBuffAdditionParam(buff).get(0);
                for (ChannelTonBingCity city : getChannelTonBingData().getCityMap().values()) {
                    if (city.getCampId() == campId) {
                        int num = Math.min(city.getTeamNum(), addTeamNum);
                        city.setTeamNum(city.getTeamNum() - num);
                    }
                }
            }
        } else if (buffTypeConfig.getType() == eTonBingBuffType.Type4_Hospital.getValue()) {
            for (ChannelTonBingUserData userData : userDataMap.values()) {
                if (userData.getCampId() == campId) {
                    recalculateBarracksHospitalWoundedMaxNum(userData.getUserId());
                    recalculateBarracksHospitalWoundedCount(userData.getUserId());
                }
            }
        } else if (buffTypeConfig.getType() == eTonBingBuffType.Type5_CityFoodProduce.getValue()) {

        } else if (buffTypeConfig.getType() == eTonBingBuffType.Type6_ReadySoldierMaxNum.getValue()) {
            for (ChannelTonBingUserData userData : userDataMap.values()) {
                if (userData.getCampId() == campId) {
                    recalculateBarracksReadyMaxNum(userData.getUserId());
                }
            }
        }
    }

    private void beforeBeginBuff(ChannelTonBingCampBuff buff) {
        int campId = buff.getCampId();
        ChannelTonBingBuffTypeConfig buffTypeConfig = config.getBuffTypeConfigMap().get(buff.getBuffId());
        if (buffTypeConfig.getType() == eTonBingBuffType.Type1_TeamSoldierMaxNum.getValue()) {

        } else if (buffTypeConfig.getType() == eTonBingBuffType.Type2_RecruitSoldierPrice.getValue()) {

        } else if (buffTypeConfig.getType() == eTonBingBuffType.Type3_CityTeamMaxNum.getValue()) {

        } else if (buffTypeConfig.getType() == eTonBingBuffType.Type4_Hospital.getValue()) {
            for (ChannelTonBingUserData userData : userDataMap.values()) {
                if (userData.getCampId() == campId) {
                    recalculateBarracksHospitalRecovery(userData.getUserId());
                }
            }
        } else if (buffTypeConfig.getType() == eTonBingBuffType.Type5_CityFoodProduce.getValue()) {
            for (ChannelTonBingUserData userData : userDataMap.values()) {
                if (userData.getCampId() == campId) {
                    recalculateCityFoodProdProduction(campId, buff.getBeginTime());
                }
            }
        } else if (buffTypeConfig.getType() == eTonBingBuffType.Type6_ReadySoldierMaxNum.getValue()) {

        }
    }

    private void afterBeginBuff(ChannelTonBingCampBuff buff) {
        int campId = buff.getCampId();
        ChannelTonBingBuffTypeConfig buffTypeConfig = config.getBuffTypeConfigMap().get(buff.getBuffId());
        if (buffTypeConfig.getType() == eTonBingBuffType.Type1_TeamSoldierMaxNum.getValue()) {
            for (ChannelTonBingUserData userData : userDataMap.values()) {
                if (userData.getCampId() == campId) {
                    refreshTeam(userData);
                }
            }
        } else if (buffTypeConfig.getType() == eTonBingBuffType.Type2_RecruitSoldierPrice.getValue()) {

        } else if (buffTypeConfig.getType() == eTonBingBuffType.Type3_CityTeamMaxNum.getValue()) {
            synchronized (lock) {
                for (ChannelTonBingCity city : getChannelTonBingData().getCityMap().values()) {
                    if (city.getCampId() == campId) {
                        city.setTeamNum(city.getTeamNum() + getBuffAdditionParam(buff).get(0));
                        if (city.getBattlefield() != null) {
                            teamToBattlefield(city, true);
                        }
                    }
                }
            }
        } else if (buffTypeConfig.getType() == eTonBingBuffType.Type4_Hospital.getValue()) {
            for (ChannelTonBingUserData userData : userDataMap.values()) {
                if (userData.getCampId() == campId) {
                    recalculateBarracksHospitalWoundedMaxNum(userData.getUserId());
                    recalculateBarracksHospitalWoundedCount(userData.getUserId());
                }
            }
        } else if (buffTypeConfig.getType() == eTonBingBuffType.Type5_CityFoodProduce.getValue()) {

        } else if (buffTypeConfig.getType() == eTonBingBuffType.Type6_ReadySoldierMaxNum.getValue()) {
            for (ChannelTonBingUserData userData : userDataMap.values()) {
                if (userData.getCampId() == campId) {
                    recalculateBarracksReadyMaxNum(userData.getUserId());
                }
            }
        }
    }

    private long getSkillSoldierAddition(long userId, int occupation) {
        ChannelTonBingUserData userData = getUserData(userId);
        List<Long> list = userData.getSoldierAddition();
        long add = 0;
        if (list.size() >= occupation) {
            add = list.get(occupation - 1);
        } else {

        }
        if(list.size() > 5){
            add += list.get(5);
        }
        return add;
    }

    // ============================== 队伍 ==============================

    /**
     * 更新门客数据（没资格也可以更新，不然游戏第一次进来没有值）
     *
     * @param first               首次同步不用设置CD，队伍士兵补满
     * @param isRefreshMaxSoldier 是否只刷新兵力上限
     */
    public int updatePatronsAbility(ChannelTonBingUserData userData, boolean first, Map<Integer, ChannelTonBingPatrons> patronsMap, List<Long> addition, boolean isRefreshMaxSoldier) {
        synchronized (lock) {

            if (!isRefreshMaxSoldier) {
                if (!first && System.currentTimeMillis() - userData.getLastSyncPatronsTime() < config.getSyncPatronsCd()) {
                    return GameErrorCode.E_TON_BING_REQ_FREQUENTLY;
                }
                for (ChannelTonBingTeam team : userData.getTeamMap().values()) {
                    if (getTeamStatus(team) != eTonBingTeamStatus.Free) {
                        return GameErrorCode.E_TON_BING_TEAM_NO_FREE;
                    }
                }
            }

            userData.setSoldierAddition(addition);
            userData.setPatronsMap(patronsMap);

            refreshTeam(userData);

            // 只刷新兵力上限不返回兵营
            if (!isRefreshMaxSoldier) {
                // 士兵回兵营
                Map<Integer, ChannelTonBingTeam> teamMap = userData.getTeamMap();
                ChannelTonBingBarracks barracks = userData.getBarracks();
                for (ChannelTonBingTeam team : teamMap.values()) {
                    long soldierNum = team.getSoldierNum();
                    team.setSoldierNum(0);
                    barracks.setReadyNum(barracks.getReadyNum() + soldierNum);
                }

                if (first) {
                    // 【首次同步】
                    // 兵补充满
                    for (ChannelTonBingTeam team : userData.getTeamMap().values()) {
                        team.setSoldierNum(team.getMaxSoldier());
                    }
                } else {
                    // 【非首次同步】
                    userData.setLastSyncPatronsTime(System.currentTimeMillis());
                }
            }
        }

        userData.setUpdateOption();

        syncBaseDataMsg(userData);
        syncBarracksMsg(userData.getUserId());
        return 0;
    }

    /**
     * 刷新队伍主将、兵力
     */
    private void refreshTeam(ChannelTonBingUserData userData) {
        Map<Integer, List<ChannelTonBingPatrons>> listMap = new ConcurrentHashMap<>();
        for (ChannelTonBingPatrons patrons : userData.getPatronsMap().values()) {
            listMap.putIfAbsent(patrons.getOccId(), new ArrayList<>());
            listMap.get(patrons.getOccId()).add(patrons);
        }

        for (ChannelTonBingTeam team : userData.getTeamMap().values()) {
            List<ChannelTonBingPatrons> list = listMap.getOrDefault(team.getOccupation(), new ArrayList<>());
            List<Integer> rankList = list.stream().sorted(Comparator.comparing(ChannelTonBingPatrons::getAbility).reversed()).map(ChannelTonBingPatrons::getId).collect(Collectors.toList());

            // 重新分配主将副将
            team.setPatronsList(rankList);

            // 重新计算上限
            team.setMaxSoldier(getTeamMaxSoldier(userData, team));
        }

        // 这个变了之后，兵营也会跟这边
        recalculateBarracksReadyMaxNum(userData.getUserId());

        userData.setUpdateOption();

        // 同步队伍数和兵营数据
        syncTeamToSelf(userData.getUserId(), new ArrayList<>(userData.getTeamMap().values()));
        syncBarracksMsg(userData.getUserId());
    }

    private long getTeamMaxSoldier(ChannelTonBingUserData userData, ChannelTonBingTeam team) {
        // 拿门客赚钱
        long totalAbility = 0L;
        List<Integer> patronsList = team.getPatronsList();
        // 只取前6个统计总赚钱（主将+副将数量=6）
        int count = Math.min(patronsList.size(), config.getTeamMainPatronsNum() + config.getTeamAuxiliaryPatronsNum());
        for (int i = 0; i < count; i++) {
            Integer patronsId = team.getPatronsList().get(i);
            ChannelTonBingPatrons patrons = userData.getPatronsMap().get(patronsId);
            if (patrons != null) {
                totalAbility += patrons.getAbility();
            }
        }
        if (totalAbility <= 0) {
            return 0;
        }

        // 后勤部队人数
        int otherPatronsCount = team.getPatronsList().size() - config.getTeamMainPatronsNum() - config.getTeamAuxiliaryPatronsNum();
        if (otherPatronsCount < 0) {
            otherPatronsCount = 0;
        }

        // 【计算】

        BigDecimal paramAbility = BigDecimal.valueOf(totalAbility);
        BigDecimal paramHouQinCount = BigDecimal.valueOf(otherPatronsCount);
        BigDecimal param1 = BigDecimal.valueOf(config.getTeamSoldierMaxNumParam().get(0));
        BigDecimal param2 = BigDecimal.valueOf(config.getTeamSoldierMaxNumParam().get(1));
        BigDecimal param3 = BigDecimal.valueOf(config.getTeamSoldierMaxNumParam().get(2));
        BigDecimal param4 = BigDecimal.valueOf(config.getTeamSoldierMaxNumParam().get(3));
        BigDecimal param5 = BigDecimal.valueOf(config.getTeamSoldierMaxNumParam().get(4));
        BigDecimal one = BigDecimal.ONE;

        // log x param2
        BigDecimal logPart = BigDecimal.valueOf(
                Math.log10(paramAbility.divide(param1, 8, BigDecimal.ROUND_DOWN).doubleValue())
        ).multiply(param2).setScale(8, BigDecimal.ROUND_DOWN);
//        logger.error("user{}计算队伍职业{}的最大队伍人数",userData.getUserId(),team.getOccupation());
//        logger.error("门客总赚钱{}",paramAbility);
//        logger.error("logPart的值是{}",logPart);
        // 1 + 后勤 x 参数3/4
        BigDecimal houQinPart = one.add(
                paramHouQinCount.multiply(
                        param3.divide(param4, 8, BigDecimal.ROUND_DOWN)
                )
        );
//        logger.error("houQinPart的值是{}",houQinPart);


        // 左边一大串
        BigDecimal leftPart = logPart.multiply(houQinPart);

        // 取max
        if (leftPart.compareTo(param5) < 0) {
            leftPart = param5;
        }

        // 拿buff加成
        int buffAddition = getBuffAddition(userData.getUserId(), eTonBingBuffType.Type1_TeamSoldierMaxNum).get(0);
        long skillSoldierAddition = getSkillSoldierAddition(userData.getUserId(), team.getOccupation());
//        logger.error("buffAddition的值是{}",buffAddition);
//        logger.error("skillSoldierAddition的值是{}",skillSoldierAddition);

        // 加buff加成
        leftPart = leftPart
                .add(BigDecimal.valueOf(skillSoldierAddition))
                .add(BigDecimal.valueOf(buffAddition));
//        logger.error("最后加buff加成后值是{}",leftPart);

        // 返回
        return leftPart.setScale(0, BigDecimal.ROUND_DOWN).longValue();
    }

    private ChannelTonBingTeam getTeamByArrive(ChannelTonBingTeamArrive arrive) {
        return getUserData(arrive.getUserId()).getTeamMap().get(arrive.getTeamId());
    }

    private eTonBingTeamStatus getTeamStatus(ChannelTonBingTeam team) {
        if (team.getRouteList().size() > 0) {
            return eTonBingTeamStatus.Move;
        }
        if (team.getCityId() == 0) { // 未分配阵营时为0
            return eTonBingTeamStatus.Free;
        }

        // 在主城时，都为空闲
        int campId = getUserData(team.getUserId()).getCampId();
        ChannelTonBingCityConfig cityConfig = config.getCityConfigMap(channelTonBingData.getWarType()).get(team.getCityId());
        if (cityConfig.getType() == 1 && cityConfig.getInitialCamp() == campId) {
            return eTonBingTeamStatus.Free;
        }

        ChannelTonBingCity city = getChannelTonBingData().getCityMap().get(team.getCityId());
        if (city.getBattlefield() != null) {
            return eTonBingTeamStatus.Battle;
        }
        return eTonBingTeamStatus.Free;
    }

    private List<ChannelTonBingTeam> getTopTeam() {
        List<ChannelTonBingTeam> result = new ArrayList<>();

        int campTopNum = config.getLightRankNum();

        for (ChannelTonBingRankListModel rankListModel : campUserRankMap.values()) {
            int num = 0;
            for (ChannelTonBingRank rank : rankListModel.getRankList()) {
                if (++num > campTopNum) {
                    break;
                }
                ChannelTonBingUserData userData = getUserData(Long.valueOf(rank.getId()));
                for (ChannelTonBingTeam team : userData.getTeamMap().values()) {
                    if (getTeamStatus(team) == eTonBingTeamStatus.Move) {
                        result.add(team);
                    }
                }
            }
        }

        return result;
    }

    private int needHelp(long userId, ChannelTonBingProto.CTonBingNeedHelpReqMsg reqMsg) {
        int cityId = reqMsg.getCityId();

        ChannelTonBingCity city = getChannelTonBingData().getCityMap().get(cityId);
        if (city == null) {
            return GameErrorCode.E_TON_BING_CITY_NO_EXIST;
        }

        ChannelTonBingUserData userData = getUserData(userId);
        if (!isAble(userData)) {
            return GameErrorCode.E_TON_BING_NO_ABLE;
        }

        synchronized (lock) {

            long now = System.currentTimeMillis();

            // 冷却
            if (now - userData.getNeedHelpTime() < config.getNeedHelpCd()) {
                return GameErrorCode.E_TON_BING_NEED_HELP_ON_CD;
            }

            // 是否战斗中
            ChannelTonBingBattlefield battlefield = city.getBattlefield();
            if (battlefield == null) {
                return GameErrorCode.E_TON_BING_NO_BATTLE;
            }

            // 看看有没有自己的阵营在里面
            int myCampId = userData.getCampId();
            boolean hasMyCamp = false;
            if (city.getCampId() == myCampId) {
                hasMyCamp = true;
            }
            if (!hasMyCamp) {
                for (ChannelTonBingTeamArrive arrive : new LinkedList<>(city.getAtkTeamList())) {
                    if (getUserData(arrive.getUserId()).getCampId() == myCampId) {
                        hasMyCamp = true;
                        break;
                    }
                }
            }
            if (!hasMyCamp) {
                for (ChannelTonBingBattlefieldPart part : battlefield.getPartMap().values()) {
                    for (ChannelTonBingBattlefieldTeam tempTeam : new LinkedList<>(part.getAtkList())) {
                        if (myCampId == getUserData(tempTeam.getTeam().getUserId()).getCampId()) {
                            hasMyCamp = true;
                            break;
                        }
                    }
                }
            }
            if (!hasMyCamp) {
                return GameErrorCode.E_TON_BING_NO_MY_CAMP_TEAM;
            }

            // 求援
            battlefield.setHelpNum(battlefield.getHelpNum() + 1);

            // 设置CD
            userData.setNeedHelpTime(now);

            // 同步城池
            addNeedSyncCity(cityId);
        }

        // 同步求援消息
        addSystemChat(eMessageType.TonBingCampHelpChat, userId, userData.getCampId(),
                (userData.getCampId() != city.getCampId() ? 1 : 2) + config.getTalkSeparator() + String.valueOf(city.getCityId()),
                System.currentTimeMillis());

        return 0;
    }

    // ============================== CMD ==============================
    private void syncServerNpcEvent(Map<Long, Map<Long, Map<Integer, Integer>>> serverSyncMap) {
        for (long serverId : serverSyncMap.keySet()) {
            ChannelTonBingProto.ChannelTonBingEventSyncMsg.Builder syncMsg = ChannelTonBingProto.ChannelTonBingEventSyncMsg.newBuilder();
            Map<Long, Map<Integer, Integer>> userMap = serverSyncMap.get(serverId);
            for (long userId : userMap.keySet()) {
                Map<Integer, Integer> eventMap = userMap.get(userId);
                ChannelTonBingProto.ChannelTonBingEventTempMsg.Builder userEventMsg = ChannelTonBingProto.ChannelTonBingEventTempMsg.newBuilder();
                userEventMsg.setUserId(userId);
                for (int eventPos : eventMap.keySet()) {
                    ChannelTonBingProto.CTonBingEventMsg.Builder eventMsg = ChannelTonBingProto.CTonBingEventMsg.newBuilder();
                    eventMsg.setPositionNo(eventPos);
                    eventMsg.setEventId(eventMap.get(eventPos));
                    userEventMsg.addEvents(eventMsg);
                }
                syncMsg.addEvents(userEventMsg);
            }
            for(int channelId : channelIdList){
                if(ChannelActivityUnionMgr.isServerIdInChannel(serverId,channelId)){
                    sendChannelPacket(GameProtocol.S_CHANNEL_CTON_BING_NPC_EVENT_SYNC, syncMsg,serverId,channelId);
                }
            }
        }
    }

    public void syncPatronsByCmd(ChannelTonBingProto.ChannelTonBingUpdatePatronsReqMsg reqMsg) {
        Map<Integer, ChannelTonBingPatrons> patronsMap = ChannelTonBingPb.parseChannelTonBingPatrons(reqMsg.getPatronsList());
        ChannelTonBingUserData userData = getUserData(reqMsg.getUserId());
        boolean isRefreshMaxSoldier = reqMsg.getIsRefreshMaxSoldier();
        boolean isfrist = false;
        if(userData.getPatronsMap().isEmpty()){
            isfrist = true;
        }
        int ret = updatePatronsAbility(userData, isfrist, patronsMap, reqMsg.getSoldierAdditionList(), isRefreshMaxSoldier);

        ChannelTonBingProto.CTonBingUpdatePatronsAbilityRespMsg.Builder respMsg = ChannelTonBingProto.CTonBingUpdatePatronsAbilityRespMsg.newBuilder();
        respMsg.setRet(ret);
        for (ChannelTonBingTeam team : userData.getTeamMap().values()) {
            respMsg.addTeam(parseTonBingTeamMsg(userData, team));
        }
        sendChannelPacket(ClientProtocol.U_CTON_BING_UPDATE_PATRONS_ABILITY, respMsg,userData);
    }

    /**
     * 设置战略目标
     */
    public void setTargetCityByCmd(long userId, ChannelTonBingProto.CTonBingSetTargetCityReqMsg reqMsg) {
        int ret = setTargetCity(userId, reqMsg);
        ChannelTonBingUserData userData = getUserData(userId);
        ChannelTonBingProto.CTonBingSetTargetCityRespMsg.Builder builder = ChannelTonBingProto.CTonBingSetTargetCityRespMsg.newBuilder();
        builder.setRet(ret);
        ChannelTonBingTeam team = userData.getTeamMap().get(reqMsg.getOccupation());
        if (team != null) {
            builder.setTeam(parseTonBingTeamMsg(userData, team));
        }
        sendChannelPacket(ClientProtocol.U_CTON_BING_SET_TARGET_CITY, builder, userData);
    }

    /**
     * 设置战略目标
     */
    public int setTargetCity(long userId, ChannelTonBingProto.CTonBingSetTargetCityReqMsg reqMsg) {
        // 参数校验
        ChannelTonBingUserData userData = getUserData(userId);
        if (!isAble(userData)) {
            return GameErrorCode.E_TON_BING_NO_ABLE;
        }
        int occupation = reqMsg.getOccupation();
        int cityId = reqMsg.getCityId();
        ChannelTonBingTeam team = userData.getTeamMap().get(occupation);
        if (team == null) {
            return GameErrorCode.E_TON_BING_TEAM_NO_EXIST;
        }

        // 时间判断(活动期和休战期可操作)
        eTonBingStage stage = this.config.getTonBingStage();
        if (stage != eTonBingStage.Stage1Time && stage != eTonBingStage.Stage1RestTime) {
            return GameErrorCode.E_TON_BING_NO_OPERATE_TIME;
        }
        // 判断是否可达
        if (cityId != 0) {
            List<Integer> cityRouteList = getRouteList(team, cityId);
            if (cityRouteList == null || cityRouteList.size() <= 1) {
                return GameErrorCode.E_TON_BING_NO_ROUTE;
            }
        }
        // 设置战略目标
        team.setTargetCityId(cityId);
        userData.setUpdateOption();
        return 0;
    }

    public void moveTeamByCmd(long userId, ChannelTonBingProto.CTonBingMoveTeamReqMsg reqMsg) {

        int ret = moveToCity(userId, reqMsg);

        ChannelTonBingUserData userData = getUserData(userId);

        // 第一次发移动请求，都当他引导了
        if (userData.getGuide() == 0) {
            userData.setGuide(1);
            syncBaseDataMsg(userData);
        }

        ChannelTonBingProto.CTonBingMoveTeamRespMsg.Builder builder = ChannelTonBingProto.CTonBingMoveTeamRespMsg.newBuilder();
        builder.setRet(ret);
        for (Integer occu : reqMsg.getOccupationList()) {
            ChannelTonBingTeam team = userData.getTeamMap().get(occu);
            if (team != null) {
                builder.addTeam(parseTonBingTeamMsg(userData, team));
            }
        }

        sendChannelPacket(ClientProtocol.U_CTON_BING_MOVE_TEAM, builder, userData);
    }

    public void speedUpByCmd(long userId, ChannelTonBingProto.CTonBingMoveSpeedUpReqMsg reqMsg) {
        int occupation = reqMsg.getOccupation();
        int times = reqMsg.getTimes();

        int ret = speedUp(userId, occupation, times);
        if (ret != 0) {
            ChannelTonBingUserData userData = getUserData(userId);
            ChannelTonBingTeam team = userData.getTeamMap().get(occupation);

            ChannelTonBingProto.CTonBingMoveSpeedUpRespMsg.Builder resp = ChannelTonBingProto.CTonBingMoveSpeedUpRespMsg.newBuilder();
            resp.setRet(ret);
            if (team != null) {
                resp.setTeam(parseTonBingTeamMsg(userData, team));
            }

            ChannelTonBingProto.ChannelTonBingMoveSpeedUpRespMsg.Builder builder = ChannelTonBingProto.ChannelTonBingMoveSpeedUpRespMsg.newBuilder();
            builder.setResp(resp);
            builder.setReqTimes(times);
            sendChannelPacket(GameProtocol.S_CTON_BING_MOVE_SPEED_UP_FROM_CHANNEL, builder, userData);

            // 发光玩家信息
//            syncLight();
        }
    }

    public void getDataByCmd(long userId, int channelId, long serverId) {
        ChannelTonBingProto.CTonBingGetDataRespMsg.Builder builder = ChannelTonBingProto.CTonBingGetDataRespMsg.newBuilder();

        ChannelTonBingUserData userData = null;
        if(containsUser(userId)){
            userData = getUserData(userId);
        }
        //所有人都可以进去了
        if (false && !isAble(userData)) {
            builder.setRet(GameErrorCode.E_TON_BING_NO_ABLE);
        } else {
            builder.setRet(0);
            if(userData != null){
                for (ChannelTonBingTeam team : userData.getTeamMap().values()) {
                    builder.addTeam(parseTonBingTeamMsg(userData, team));
                }
                builder.setBarracks(parseTonBingBarracksMsg(userData.getUserId()));
            }
            for (ChannelTonBingCity city : getChannelTonBingData().getCityMap().values()) {
                builder.addCity(parseTonBingCityMsg(city));
            }
            for (ChannelTonBingTeam team : getTopTeam()) {
                builder.addTeam(parseTonBingTeamMsg(getUserData(team.getUserId()), team)); // 前几名也加到这边
            }
            for (ChannelTonBingCityEvent event : getChannelTonBingData().getCityEventMap().values()) {
                builder.addEvent(parseTonBingCityEventMsg(event));
            }
            if(userData != null){
                List<ChannelTonBingCampBuff> buffList = getChannelTonBingData().getCampBuffMap().get(userData.getCampId());
                if (buffList != null) {
                    for (ChannelTonBingCampBuff buff : new ArrayList<>(buffList)) {
                        builder.addBuff(parseTonBingBuffMsg(buff));
                    }
                }
                ChannelTonBingBuffTypeConfig rankBuffConfig = getRankBuffInfo(userData.getCampId());
                if(rankBuffConfig != null){
                    ChannelTonBingCampBuff rankBuff = new ChannelTonBingCampBuff();
                    rankBuff.setBuffId(rankBuffConfig.getId());
                    rankBuff.setCampId(userData.getCampId());
                    rankBuff.setCityId(0);
                    builder.addBuff(parseTonBingBuffMsg(rankBuff));
                }
                for (Map.Entry<Integer, Integer> entry : userData.getEventMap().entrySet()) {
                    ChannelTonBingProto.CTonBingEventMsg.Builder eventMsg = ChannelTonBingProto.CTonBingEventMsg.newBuilder();
                    eventMsg.setPositionNo(entry.getKey());
                    eventMsg.setEventId(entry.getValue());
                    builder.addNpcEvent(eventMsg);
                }
            }
        }

        addWatchPlayer(userId);
        ChannelMessageHelper.sendGamePacket(ClientProtocol.U_CTON_BING_GET_DATA, builder, channelId, serverId, userId);
    }

    public void getMyTeamPosByCmd(long userId, ChannelTonBingProto.CTonBingGetBattlefieldMyTeamReqMsg reqMsg) {
        ChannelTonBingUserData userData = getUserData(userId);
        int cityId = reqMsg.getCityId();

        // 返回值
        ChannelTonBingProto.CTonBingGetBattlefieldMyTeamRespMsg.Builder builder = ChannelTonBingProto.CTonBingGetBattlefieldMyTeamRespMsg.newBuilder();

        // 判断资格
        int campId = getUserData(userId).getCampId();
        if (campId <= 0) {
            builder.setRet(GameErrorCode.E_TON_BING_NO_ABLE);
            sendChannelPacket(ClientProtocol.U_CTON_BING_GET_MY_TEAM_POS, builder, userData);
            return;
        }

        builder.setRet(0);

        ChannelTonBingCity city = getChannelTonBingData().getCityMap().get(cityId);
        if (city != null) {
            ChannelTonBingBattlefield battlefield = city.getBattlefield();
            if (battlefield != null) {

                // 战场内的
                for (ChannelTonBingBattlefieldPart part : battlefield.getPartMap().values()) {
                    LinkedList<ChannelTonBingBattlefieldTeam> list = city.getCampId() == campId ? part.getDefList() : part.getAtkList();
                    int no = 0;
                    for (ChannelTonBingBattlefieldTeam team : new LinkedList<>(list)) {
                        no++;
                        if (team.getTeam() != null && team.getTeam().getUserId() == userId) {
                            builder.addTeamPos(ChannelTonBingProto.CTonBingBattlefieldMyTeamMsg.newBuilder()
                                    .setPartId(part.getPartId())
                                    .setTeamId(team.getTeam().getTeamId())
                                    .setPos(no));
                        }
                    }
                }

                // 加一下排队中的
                LinkedList<ChannelTonBingTeamArrive> arriveLinkedList = city.getCampId() == campId ? city.getDefTeamList() : city.getAtkTeamList();
                int no = 0;
                for (ChannelTonBingTeamArrive arrive : new LinkedList<>(arriveLinkedList)) {
                    no++;
                    if (arrive.getUserId() == userId) {
                        builder.addTeamPos(ChannelTonBingProto.CTonBingBattlefieldMyTeamMsg.newBuilder()
                                .setPartId(0)
                                .setTeamId(arrive.getTeamId())
                                .setPos(no));
                    }
                }
            }
        }

        sendChannelPacket(ClientProtocol.U_CTON_BING_GET_MY_TEAM_POS, builder, userData);
    }

    public void addUserWarReport(ChannelTonBingUserWarReport report) {
        synchronized (lock) {
            int size = needInsertUserReportList.size();
            int no = size == 0 ? 1 : needInsertUserReportList.get(size - 1).getNo();
            report.setNo(no + 1);
            userWarReportsList.add(report);
            needInsertUserReportList.add(report);
        }

        // 红点
        if (report.getUserIdDef() != 0) {
            addRedDot(getUserData(report.getUserIdDef()), eTonBingRedDotType.Type2_UserWarReport);
        }
        addRedDot(getUserData(report.getUserIdAct()), eTonBingRedDotType.Type2_UserWarReport);

    }

    public void addCityWarReport(ChannelTonBingCityWarReport report) {
        synchronized (lock) {
            cityWarReportList.add(report);
            needInsertCityReportList.add(report);
        }

        // 红点
        for (Integer campId : report.getAtkCampId()) {
            for (ChannelTonBingUserData userData : userDataMap.values()) {
                if (userData.getCampId() == campId) {
                    addRedDot(userData, eTonBingRedDotType.Type3_CityWarReport);
                }
            }
        }
        if (report.getDefCampId() != 0) {
            for (ChannelTonBingUserData userData : userDataMap.values()) {
                if (userData.getCampId() == report.getDefCampId()) {
                    addRedDot(userData, eTonBingRedDotType.Type3_CityWarReport);
                }
            }
        }
    }

    //获得城池战报
    public void cityWarReport(long userId) {
        ChannelTonBingUserData userData = getUserData(userId);
        ChannelTonBingProto.CTonBingCityWarReportRespMsg.Builder respMsg = ChannelTonBingProto.CTonBingCityWarReportRespMsg.newBuilder();
        respMsg.setRet(0);
        List<ChannelTonBingCityWarReport> list = new ArrayList<>(cityWarReportList);
        int count = 0;
        for (int i = list.size() - 1; i >= 0; i--) {
            ChannelTonBingCityWarReport report = list.get(i);
            if (count >= config.getUserReportNum()) {
                break;
            }
            if (report.getDefCampId() == userData.getCampId() || report.getAtkCampId().contains(userData.getCampId())) {
                count++;
                ChannelTonBingProto.CTonBingCityWarReportMsg.Builder tempMsg = ChannelTonBingProto.CTonBingCityWarReportMsg.newBuilder();
                for (int atkCampId : report.getAtkCampId()) {
                    tempMsg.addAtkCampId(atkCampId);
                }
                tempMsg.setDefCampId(report.getDefCampId());
                tempMsg.setType(report.getType());
                tempMsg.setCityId(report.getCityId());
                tempMsg.setTime(report.getTime());
                for (ChannelTonBingBattlefieldRank atkRank : report.getAtkRankList()) {
                    tempMsg.addAtkRank(parseTonBingBattlefieldRankMsg(atkRank));
                }
                for (ChannelTonBingBattlefieldRank defRank : report.getDefRankList()) {
                    tempMsg.addDefRank(parseTonBingBattlefieldRankMsg(defRank));
                }
                tempMsg.setWinCampId(report.getWinCampId());
                respMsg.addList(tempMsg);
            }
        }
        sendChannelPacket(ClientProtocol.U_CTON_BING_CITY_WAR_REPORT, respMsg, userData);
    }

    //获得个人战报
    public void userWarReport(long userId) {
        ChannelTonBingUserData userData = getUserData(userId);
        ChannelTonBingProto.CTonBingUserWarReportRespMsg.Builder respMsg = ChannelTonBingProto.CTonBingUserWarReportRespMsg.newBuilder();
        respMsg.setRet(0);
        List<ChannelTonBingUserWarReport> list = new ArrayList<>(userWarReportsList);
        UserBaseInfo userBaseInfo = ChannelUserMgr.getUserBaseInfo(userId);
        String myNickName = "";
        if (userBaseInfo != null) {
            myNickName = userBaseInfo.getNickName();
        }
        int count = 0;
        for (int i = list.size() - 1; i >= 0; i--) {
            ChannelTonBingUserWarReport report = list.get(i);
            if (count >= config.getCityReportNum()) {
                break;
            }
            if (report.getUserIdAct() == userId) {
                count++;
                ChannelTonBingProto.CTonBingUserWarReportMsg.Builder tempMsg = ChannelTonBingProto.CTonBingUserWarReportMsg.newBuilder();
                tempMsg.setNo(report.getNo());
                tempMsg.setMyPatronsId(report.getActPatronsId());
                tempMsg.setMySoldierNum(report.getActSoldierNum());
                tempMsg.setOtherPatronsId(report.getDefPatronsId());
                tempMsg.setOtherSoldierNum(report.getDefSoldierNum());
                tempMsg.setTime(report.getTime());
                tempMsg.setAddZhanGong(report.getActZhanGong());
                tempMsg.setKillSoldierNum(report.getDefDieSoldierNum());
                tempMsg.setCityId(report.getCityId());
                tempMsg.setIsWin(report.getWinUserId() == 2);
                tempMsg.setCostSoldierNum(report.getActDieSoldierNum());
                tempMsg.setMyAddition(report.getActAddition());
                tempMsg.setOtherAddition(report.getDefAddition());
                tempMsg.setMyNickName(myNickName);
                String otherNickName = "";
                if (report.getUserIdDef() != 0) {
                    UserBaseInfo otherUserBaseInfo = ChannelUserMgr.getUserBaseInfo(report.getUserIdDef());
                    if (otherUserBaseInfo != null) {
                        otherNickName = otherUserBaseInfo.getNickName();
                    }
                }
                tempMsg.setOtherNickName(otherNickName);
                respMsg.addList(tempMsg);
            } else if (report.getUserIdDef() == userId) {
                count++;
                ChannelTonBingProto.CTonBingUserWarReportMsg.Builder tempMsg = ChannelTonBingProto.CTonBingUserWarReportMsg.newBuilder();
                tempMsg.setNo(report.getNo());
                tempMsg.setMyPatronsId(report.getDefPatronsId());
                tempMsg.setMySoldierNum(report.getDefSoldierNum());
                tempMsg.setOtherPatronsId(report.getActPatronsId());
                tempMsg.setOtherSoldierNum(report.getActSoldierNum());
                tempMsg.setTime(report.getTime());
                tempMsg.setAddZhanGong(report.getDefZhanGong());
                tempMsg.setKillSoldierNum(report.getActDieSoldierNum());
                tempMsg.setCityId(report.getCityId());
                tempMsg.setIsWin(report.getWinUserId() != 2);
                tempMsg.setCostSoldierNum(report.getDefDieSoldierNum());
                tempMsg.setMyAddition(report.getDefAddition());
                tempMsg.setOtherAddition(report.getActAddition());
                tempMsg.setMyNickName(myNickName);
                String otherNickName = "";
                if (report.getUserIdAct() != 0) {
                    UserBaseInfo otherUserBaseInfo = ChannelUserMgr.getUserBaseInfo(report.getUserIdAct());
                    if (otherUserBaseInfo != null) {
                        otherNickName = otherUserBaseInfo.getNickName();
                    }
                }
                tempMsg.setOtherNickName(otherNickName);
                respMsg.addList(tempMsg);
            }
        }
        sendChannelPacket(ClientProtocol.U_CTON_BING_USER_WAR_REPORT, respMsg, userData);
    }

    /**
     * 任命指挥官
     */
    public void campCommanderAppoint(long userId, ChannelTonBingProto.ChannelTonBingCampCommanderAppointReqMsg reqMsg) {
        ChannelTonBingProto.CTonBingCampCommanderAppointRespMsg.Builder respMsg = ChannelTonBingProto.CTonBingCampCommanderAppointRespMsg.newBuilder();
        respMsg.setRet(0);
        ChannelTonBingUserData userData = getUserData(userId);
        long maxZhanGongUserId = getCampMaxZhanGongUserId(userData.getCampId());
        if (userId != maxZhanGongUserId || userData.getZhanGong() < config.getAppointScoreLimit()) {
            respMsg.setRet(GameErrorCode.E_TON_BING_CAN_NOT_SET_COMMANDER);
            sendChannelPacket(ClientProtocol.U_CTON_BING_CAMP_COMMANDER, respMsg, userData);
            return;
        }
        ChannelTonBingUserData appointUserData = getUserData(reqMsg.getUserId());//任命的玩家
        synchronized (lock) {
            // 只能任命当前阵营的人
            if (appointUserData.getCampId() != userData.getCampId()) {
                respMsg.setRet(GameErrorCode.E_TON_BING_COMMANDER_ONLY_SET_THE_SAME_CAMP);
                sendChannelPacket(ClientProtocol.U_CTON_BING_CAMP_COMMANDER, respMsg, userData);
                return;
            }
            ChannelTonBingCampMatchData campData = campMatchDataMap.get(userData.getCampId());
            ChannelTonBingCommanderData commanderData = campData.getCommanderData();
            if (commanderData == null) {
                commanderData = new ChannelTonBingCommanderData();
            }
            commanderData.setUserId(appointUserData.getUserId());
            campData.setCommanderData(commanderData);
            sendChannelPacket(ClientProtocol.U_CTON_BING_CAMP_COMMANDER, respMsg, userData);
            // 同步信息给阵营玩家
            syncCommanderInfoToCamp(userData.getCampId());
        }
    }

    /**
     * 指挥官设置信息
     */
    public void commanderSet(long userId, ChannelTonBingProto.ChannelTonBingCommanderSetReqMsg reqMsg) {
        ChannelTonBingProto.CTonBingCommanderSetRespMsg.Builder respMsg = ChannelTonBingProto.CTonBingCommanderSetRespMsg.newBuilder();
        respMsg.setRet(0);
        ChannelTonBingUserData userData = getUserData(userId);
        // 判断是否为指挥官
        synchronized (lock) {
            ChannelTonBingCampMatchData campData = campMatchDataMap.get(userData.getCampId());
            ChannelTonBingCommanderData commanderData = campData.getCommanderData();
            if (commanderData == null) {
                commanderData = new ChannelTonBingCommanderData();
            }
            long maxZhanGongUserId = getCampMaxZhanGongUserId(userData.getCampId());
            // 只有战功第一的或者指挥官才可设置
            boolean isMaxZhangong = maxZhanGongUserId == userId && userData.getZhanGong() >= config.getAppointScoreLimit();
            boolean isCommander = commanderData.getUserId() == userId;
            if (!isMaxZhangong && !isCommander) {
                respMsg.setRet(GameErrorCode.E_TON_BING_NOT_COMMANDER);
                sendChannelPacket(ClientProtocol.U_CTON_BING_COMMANDER_SET, respMsg, userData);
                return;
            }
            // 设置信息
            boolean isNeedSync = false;
            ChannelTonBingProto.CTonBingCommanderSetReqMsg commanderSetReqMsg = reqMsg.getCommanderSetReqMsg();
            if (commanderSetReqMsg.hasNotice()) {
                commanderData.setNotice(commanderSetReqMsg.getNotice());
                isNeedSync = true;
            }
            if (commanderSetReqMsg.hasMajorCityId()) {
                commanderData.setMajorCityId(commanderSetReqMsg.getMajorCityId());
                isNeedSync = true;
            }
            if (commanderSetReqMsg.hasMinorCityId()) {
                commanderData.setMinorCityId(commanderSetReqMsg.getMinorCityId());
                isNeedSync = true;
            }
            sendChannelPacket(ClientProtocol.U_CTON_BING_COMMANDER_SET, respMsg, userData);
            if (isNeedSync) {
                campData.setCommanderData(commanderData);
                // 同步信息给阵营玩家
                syncCommanderInfoToCamp(userData.getCampId());
            }
        }
    }

    /**
     * 获取同阵营的用户数据
     */
    public void getCampUserData(long userId, ChannelTonBingProto.ChannelTonBingCampUserDataReqMsg reqMsg) {
        ChannelTonBingProto.CTonBingCampUserDataRespMsg.Builder respMsg = ChannelTonBingProto.CTonBingCampUserDataRespMsg.newBuilder();
        respMsg.setRet(0);
        ChannelTonBingUserData userData = getUserData(userId);
        if (!isAble(userData)) {
            respMsg.setRet(GameErrorCode.E_TON_BING_NO_ABLE);
            sendChannelPacket(ClientProtocol.U_CTON_BING_CAMP_USER_DATA, respMsg, userData);
            return;
        }
        long maxZhanGongUserId = getCampMaxZhanGongUserId(userData.getCampId());
        for (ChannelTonBingUserData tonBingUserData : userDataMap.values()) {
            // 获取阵营
            if (tonBingUserData.getCampId() == userData.getCampId()) {
                UserBaseInfo userBaseInfo = ChannelUserMgr.getUserBaseInfo(tonBingUserData.getUserId());
                if (userBaseInfo != null) {
                    ChannelTonBingProto.CTonBingCampUserDataTemp.Builder temp = ChannelTonBingProto.CTonBingCampUserDataTemp.newBuilder();
                    temp.setUserId(tonBingUserData.getUserId());
                    temp.setPlayerBaseData(PlayerBasePb.parsePlayerBaseTempMsg(userBaseInfo));
                    temp.setZhangong(tonBingUserData.getZhanGong());
                    temp.setIsMaxZhanGong(tonBingUserData.getUserId() == maxZhanGongUserId);
                    respMsg.addCampUserData(temp);
                }
            }
        }
        sendChannelPacket(ClientProtocol.U_CTON_BING_CAMP_USER_DATA, respMsg, userData);
    }


    /**
     * 添加伤兵
     */
    public void addBarracksWoundedSoldier(long userId, long num) {
        barracksCalc(userId);
        ChannelTonBingUserData userData = getUserData(userId);
        synchronized (lock) {
            ChannelTonBingBarracks barracks = userData.getBarracks();

            // 日志用
            String logBeforeBarracks = JSON.toJSONString(barracks);

            long woundedNum = barracks.getWoundedNum();
            long woundedMaxNum = barracks.getWoundedMaxNum();
            if (woundedNum >= woundedMaxNum) {
                // 到达上限，不操作
            } else if (woundedNum + num >= woundedMaxNum) {
                // 加完后超过上限，设置为上限
                barracks.setWoundedNum(woundedMaxNum);
            } else {
                // 正常添加
                barracks.setWoundedNum(woundedNum + num);
            }
            barracks.setRescueSpeed(config.getWoundedRescueTime());
            barracks.setRescueCount(getBarracksWoundedCount(userId));
            if (barracks.getRescueTime() == 0) {
                barracks.setRescueTime(System.currentTimeMillis());
            }
            userData.setUpdateOption();

            // 日志
            ChannelAutoLogMgr.add(new LogChannelTonBingSoldierChange(
                    config.getActivityInfo().getActivityId(),
                    userId,
                    5,
                    num,
                    -1,
                    logBeforeBarracks,
                    JSON.toJSONString(barracks),
                    channelTonBingData.getMatchId(),
                    channelTonBingData.getTurn(),
                    channelTonBingData.getWarType()
            ));
        }
    }

    public void getUserBarracksInfo(long userId) {
        barracksCalc(userId);
        ChannelTonBingProto.CTonBingGetBarracksRespMsg.Builder respMsg = ChannelTonBingProto.CTonBingGetBarracksRespMsg.newBuilder();
        ChannelTonBingUserData userData = getUserData(userId);
        if (!isAble(userData)) {
            respMsg.setRet(GameErrorCode.E_TON_BING_NO_ABLE);
            sendChannelPacket(ClientProtocol.U_CTON_BING_GET_BARRACKS, respMsg, userData);
            return;
        }
        respMsg.setRet(0);
        respMsg.setBarracks(parseTonBingBarracksMsg(userId));
        sendChannelPacket(ClientProtocol.U_CTON_BING_GET_BARRACKS, respMsg, userData);
    }

    /**
     * 计算兵营的产出
     */
    private void barracksCalc(long userId) {
        long nowTime = System.currentTimeMillis();
        ChannelTonBingUserData userData = getUserData(userId);
        ChannelTonBingBarracks barracks = userData.getBarracks();

        synchronized (lock) {
            if (barracks.getCreateNum() > 0) {
                long createTime = barracks.getCreateTime();
                // 征兵了几秒
                long second = (nowTime - createTime) / DateHelper.SECOND_MILLIONS;
                // 征兵了几个
                long createNumber = second * barracks.getCreateSpeed();
                // 可以全部征完
                if (createNumber >= barracks.getCreateNum()) {
                    createNumber = barracks.getCreateNum();
                }

                barracks.setCreateNum(barracks.getCreateNum() - createNumber);
                if (barracks.getCreateNum() == 0) {
                    barracks.setCreateTime(0);
                    //全部征兵完了设置一下速度和花费和数量为0
                    barracks.setCreateSpeed(0);
                    barracks.setCreateCost(0);
                    barracks.setCreateCount(0);
                } else {
                    barracks.setCreateTime(barracks.getCreateTime() + second * DateHelper.SECOND_MILLIONS);
                }
                barracks.setReadyNum(barracks.getReadyNum() + createNumber);
                userData.setUpdateOption();
            }

            if (barracks.getWoundedNum() > 0) {
                long rescueTime = barracks.getRescueTime();
                // 修复次数
                long rescueNumber = (nowTime - rescueTime) / barracks.getRescueSpeed() * barracks.getRescueCount();
                // 时间到了全部伤兵时间
                if (rescueNumber >= barracks.getWoundedNum()) {
                    rescueNumber = barracks.getWoundedNum();
                }
                barracks.setWoundedNum(barracks.getWoundedNum() - rescueNumber);
                if (barracks.getWoundedNum() == 0) {
                    barracks.setRescueTime(0);
                } else {
                    barracks.setRescueTime(barracks.getRescueTime() + (rescueNumber / barracks.getRescueCount()) * barracks.getRescueSpeed());
                }
                barracks.setRescueNum(barracks.getRescueNum() + rescueNumber);
                userData.setUpdateOption();
            }
        }
    }

    /**
     * 兵营玩家道具操作
     *
     * @param type 1扣消耗，2返回预扣除，3不操作，4加道具
     */
    private void sendBarracksOpMsg(long serverId, long userId, int type, String cost, ChannelTonBingProto.CTonBingRecruitSoldierRespMsg respMsg) {
        ChannelTonBingProto.ChannelTonBingBarracksOpMsg.Builder builder = ChannelTonBingProto.ChannelTonBingBarracksOpMsg.newBuilder();
        builder.setActivityId(config.getActivityInfo().getActivityId());
        builder.setType(type);
        builder.setCost(cost);
        if (respMsg != null) {
            builder.setResMsg(respMsg);
        }
        sendChannelPacket(GameProtocol.S_CHANNEL_CTON_BING_BARRACKS_OP, builder,getUserData(userId));
    }

    /**
     * 获取招募所需银两
     */
    public void recruitSoldierSliver(long serverId, long userId, ChannelTonBingProto.ChannelTonBingRecruitSoldierReqMsg reqMsg) {
        long speed = reqMsg.getSpeed();
        long num = reqMsg.getNum();

        Map<Long, Long> recruitSoldierMap = config.getRecruitSoldierMap();
        ChannelTonBingUserData userData = getUserData(userId);
        ChannelTonBingBarracks barracks = userData.getBarracks();

        ChannelTonBingProto.CTonBingRecruitSoldierRespMsg.Builder respMsg = ChannelTonBingProto.CTonBingRecruitSoldierRespMsg.newBuilder();

        // 没有找到对应速度的招募
        if (!recruitSoldierMap.containsKey(speed)) {
            respMsg.setRet(GameErrorCode.E_TON_BING_BARRACKS_SPEED);
            sendChannelPacket(ClientProtocol.U_CTON_BING_RECRUIT_SOLDIER, respMsg, userData);
            return;
        }

        // 上一次的招募还没有结束
        barracksCalc(userId);
        if (barracks.getCreateNum() != 0) {
            respMsg.setRet(GameErrorCode.E_TON_BING_BARRACKS_ING);
            sendChannelPacket(ClientProtocol.U_CTON_BING_RECRUIT_SOLDIER, respMsg, userData);
            return;
        }

        // 超出招募上限
        if (num + barracks.getReadyNum() > barracks.getReadyMaxNum()) {
            respMsg.setRet(GameErrorCode.E_TON_BING_BARRACKS_FULL);
            sendChannelPacket(ClientProtocol.U_CTON_BING_RECRUIT_SOLDIER, respMsg, userData);
            return;
        }

        // 每秒银两
        BigInteger silver = calcRecruitPerSecondSilver(userId, speed);

        // 每个兵多少钱
        BigInteger silverPerSoldier = new BigDecimal(silver).divide(BigDecimal.valueOf(speed), 0, BigDecimal.ROUND_UP).toBigInteger();

        // 总银两  = 士兵数 * 总银两
        BigInteger totalSilver = silverPerSoldier.multiply(BigInteger.valueOf(num));

        ChannelTonBingProto.ChannelTonBingRecruitSoldierReqMsg.Builder respServerMsg = reqMsg.toBuilder();
        respServerMsg.setSilver(totalSilver.toString());
        sendChannelPacket(GameProtocol.S_CHANNEL_CTON_BING_CALL_SOLDIER_SLIVER_RESP, respServerMsg,userData);
    }

    /**
     * 计算招募每秒消耗银两（含BUFF）
     */
    private BigInteger calcRecruitPerSecondSilver(long userId, long speed) {
        // 拿配置
        Map<Long, Long> recruitSoldierMap = config.getRecruitSoldierMap();
        long silver = recruitSoldierMap.get(speed);
        // 加buff
        Integer buffAddition = getBuffAddition(userId, eTonBingBuffType.Type2_RecruitSoldierPrice).get(0);
        // 计算结果
        BigInteger result = BigDecimal.valueOf(silver)
                .multiply(BigDecimal.valueOf(1000 - buffAddition))
                .divide(BigDecimal.valueOf(1000), 0, BigDecimal.ROUND_UP).toBigInteger();
        // 容错
        if (result.compareTo(BigInteger.ONE) <= 0) {
            result = BigInteger.ONE;
        }
        return result;
    }

    /**
     * 招募士兵
     */
    public void recruitSoldier(long serverId, long userId, ChannelTonBingProto.ChannelTonBingRecruitSoldierReqMsg reqMsg) {

        barracksCalc(userId);
        ChannelTonBingUserData userData = getUserData(userId);
        ChannelTonBingBarracks barracks = userData.getBarracks();

        ChannelTonBingProto.CTonBingRecruitSoldierRespMsg.Builder respMsg = ChannelTonBingProto.CTonBingRecruitSoldierRespMsg.newBuilder();
        respMsg.setRet(0);

        int type = reqMsg.getType();
        long speed = reqMsg.getSpeed();
        long num = reqMsg.getNum(); // 士兵数量 or 兵符数量

        String costStr = "";

        synchronized (lock) {

            // 日志用
            String logBeforeBarracks = JSON.toJSONString(barracks);

            // 【取消银两招兵】
            if (type == 1 && speed == 0) {
                if (barracks.getCreateNum() <= 0) {
                    respMsg.setRet(GameErrorCode.E_TON_BING_BARRACKS_NO_NEED_CANCEL);
                    sendBarracksOpMsg(serverId, userId, 3, costStr, respMsg.build());
                    return;
                }

                // 剩余银两 = 每秒银两 x ( 剩余士兵数 / 每秒士兵数 )
                long sliver = barracks.getCreateCost();
                BigInteger cost = BigDecimal.valueOf(sliver).multiply(
                        BigDecimal.valueOf(barracks.getCreateNum()).divide(BigDecimal.valueOf(barracks.getCreateSpeed()), 8, BigDecimal.ROUND_DOWN)
                ).setScale(0, BigDecimal.ROUND_DOWN).toBigInteger();

                // 设置返还银两
                costStr = config.getGameMoneySilver()+ "=" + cost.toString();

                // 修改兵营
                barracks.setCreateCost(0);
                barracks.setCreateCount(0);
                barracks.setCreateSpeed(0);
                barracks.setCreateTime(0);
                barracks.setCreateNum(0);
                userData.setUpdateOption();

                // 返回
                respMsg.setBarracks(parseTonBingBarracksMsg(userId));
                sendBarracksOpMsg(serverId, userId, 4, costStr, respMsg.build());

                // 日志
                ChannelAutoLogMgr.add(new LogChannelTonBingSoldierChange(
                        config.getActivityInfo().getActivityId(),
                        userId,
                        4,
                        num,
                        -1,
                        logBeforeBarracks,
                        JSON.toJSONString(barracks),
                        channelTonBingData.getMatchId(),
                        channelTonBingData.getTurn(),
                        channelTonBingData.getWarType()
                ));
                return;
            }


            if (type == 1 && speed != 0) {
                // 【银两募兵】

                // 没有找到对应速度的招募
                Map<Long, Long> recruitSoldierMap = config.getRecruitSoldierMap();
                if (!recruitSoldierMap.containsKey(speed)) {
                    respMsg.setRet(GameErrorCode.E_TON_BING_BARRACKS_SPEED);
                    sendBarracksOpMsg(serverId, userId, 2, costStr, respMsg.build());
                    return;
                }

                // 上一次的招募还没有结束
                if (barracks.getCreateNum() != 0) {
                    respMsg.setRet(GameErrorCode.E_TON_BING_BARRACKS_ING);
                    sendBarracksOpMsg(serverId, userId, 2, costStr, respMsg.build());
                    return;
                }

                // 超上限
                if (num + barracks.getReadyNum() > barracks.getReadyMaxNum()) {
                    respMsg.setRet(GameErrorCode.E_TON_BING_BARRACKS_FULL);
                    sendBarracksOpMsg(serverId, userId, 2, costStr, respMsg.build());
                    return;
                }

                // 设置消耗
                costStr = config.getGameMoneySilver()+ "=" + reqMsg.getSilver();

                // 每个兵的消耗
                BigInteger soldierSilver = new BigDecimal(reqMsg.getSilver()).divide(BigDecimal.valueOf(num), 0, BigDecimal.ROUND_UP).toBigInteger();
                // 每秒消耗
                BigInteger secondSilver = soldierSilver.multiply(BigInteger.valueOf(speed));

                // 改数据
                barracks.setCreateNum(num);
                barracks.setCreateTime(System.currentTimeMillis());
                barracks.setCreateSpeed(speed);
                barracks.setCreateCost(secondSilver.longValue());
                barracks.setCreateCount(1);
                userData.setUpdateOption();

            } else if (type == 2) {
                // 【伤兵救治】
                if (barracks.getRescueNum() < num) {
                    respMsg.setRet(GameErrorCode.E_TON_BING_BARRACKS_HOSPITAL_NO_ENOUGH);
                    sendBarracksOpMsg(serverId, userId, 3, costStr, respMsg.build());
                    return;
                }
                barracks.setReadyNum(barracks.getReadyNum() + num);
                barracks.setRescueNum(barracks.getRescueNum() - num);
                userData.setUpdateOption();

            } else if (type == 3) {
                // 【兵符募兵】

                // 设置消耗
                costStr = config.getBingFuItem() + "=" + num;

                barracks.setReadyNum(barracks.getReadyNum() + num * reqMsg.getBingFuNum());
                userData.setUpdateOption();

            } else {
                // 【参数错误】

                respMsg.setRet(GameErrorCode.E_TON_BING_PARAM_ERR);
                sendBarracksOpMsg(serverId, userId, 2, costStr, respMsg.build());
                return;
            } // 【加类型下面日志要改】

            respMsg.setBarracks(parseTonBingBarracksMsg(userId));
            sendBarracksOpMsg(serverId, userId, 1, costStr, respMsg.build());

            // 日志
            ChannelAutoLogMgr.add(new LogChannelTonBingSoldierChange(
                    config.getActivityInfo().getActivityId(),
                    userId,
                    type, // 123刚好是对应的
                    num,
                    type == 1 ? speed : -1,
                    logBeforeBarracks,
                    JSON.toJSONString(barracks),
                    channelTonBingData.getMatchId(),
                    channelTonBingData.getTurn(),
                    channelTonBingData.getWarType()
            ));
        }
    }

    public void changeSoldier(long userId, ChannelTonBingProto.ChannelTonBingChangeTeamSoldierReqMsg reqMsg) {
        ChannelTonBingProto.CTonBingChangeTeamSoldierRespMsg.Builder respMsg = ChannelTonBingProto.CTonBingChangeTeamSoldierRespMsg.newBuilder();
        respMsg.setRet(0);
        ChannelTonBingUserData userData = getUserData(userId);
        if (!isAble(userData)) {
            respMsg.setRet(GameErrorCode.E_TON_BING_NO_ABLE);
            sendChannelPacket(ClientProtocol.U_CTON_BING_CHANGE_TEAM_SOLDIER, respMsg, userData);
            return;
        }

        barracksCalc(userId);

        Map<Integer, ChannelTonBingTeam> teamMap = userData.getTeamMap();
        long allValue = 0L;
        synchronized (lock) {

            // 日志用
            String logBeforeBarracks = JSON.toJSONString(userData.getBarracks());

            for (ChannelTonBingProto.CTonBingChangeTeamSoldierMsg changeMsg : reqMsg.getChangeList()) {
                int occType = changeMsg.getOccupation();
                ChannelTonBingTeam team = teamMap.get(occType);
                if (getTeamStatus(team) != eTonBingTeamStatus.Free) {
                    respMsg.setRet(GameErrorCode.E_TON_BING_TEAM_CHANGE_SOLDIER_NO_FREE);
                    sendChannelPacket(ClientProtocol.U_CTON_BING_CHANGE_TEAM_SOLDIER, respMsg, userData);
                    return;
                }
                if (changeMsg.getSoldierNum() > team.getMaxSoldier() || changeMsg.getSoldierNum() < 0) {
                    respMsg.setRet(GameErrorCode.E_TON_BING_TEAM_CHANGE_SOLDIER_MAX);
                    sendChannelPacket(ClientProtocol.U_CTON_BING_CHANGE_TEAM_SOLDIER, respMsg, userData);
                    return;
                }
                allValue = allValue + (changeMsg.getSoldierNum() - team.getSoldierNum());
            }
            if (allValue > userData.getBarracks().getReadyNum()) {
                respMsg.setRet(GameErrorCode.E_TON_BING_TEAM_CHANGE_SOLDIER_NO_ENOUGH);
                sendChannelPacket(ClientProtocol.U_CTON_BING_CHANGE_TEAM_SOLDIER, respMsg, userData);
                return;
            }
            for (ChannelTonBingProto.CTonBingChangeTeamSoldierMsg changeMsg : reqMsg.getChangeList()) {
                int occType = changeMsg.getOccupation();
                ChannelTonBingTeam team = teamMap.get(occType);
                team.setSoldierNum(changeMsg.getSoldierNum());
            }
            userData.setUpdateOption();

            for (ChannelTonBingTeam team : teamMap.values()) {
                respMsg.addTeam(parseTonBingTeamMsg(userData, team));
            }
            ChannelTonBingBarracks barracks = userData.getBarracks();
            barracks.setReadyNum(userData.getBarracks().getReadyNum() - allValue);
            userData.setUpdateOption();
            respMsg.setBarracks(parseTonBingBarracksMsg(userId));
            sendChannelPacket(ClientProtocol.U_CTON_BING_CHANGE_TEAM_SOLDIER, respMsg, userData);

            // 日志
            ChannelAutoLogMgr.add(new LogChannelTonBingSoldierChange(
                    config.getActivityInfo().getActivityId(),
                    userId,
                    6,
                    allValue,
                    -1,
                    logBeforeBarracks,
                    JSON.toJSONString(barracks),
                    channelTonBingData.getMatchId(),
                    channelTonBingData.getTurn(),
                    channelTonBingData.getWarType()
            ));
        }
    }

    public void makeNpcEvent() {
        long npcEventAddTime = getChannelTonBingData().getNpcEventAddTime();
        long nowTime = System.currentTimeMillis();
        long startTime = DateHelper.getTodayZeroTimeStamp() + config.getNpcEventBeginTime();

        // 今天未开始 || 小于上次生成时间 || 活动结束了
        if (nowTime < startTime || nowTime < npcEventAddTime || nowTime > config.getActivityInfo().getEndTime() * 1000L) {
            return;
        }

        for (ChannelTonBingUserData userData : userDataMap.values()) {
            if (userData.getEventResetTime() != DateHelper.getTodayZeroTimeStamp() + config.getNpcEventBeginTime()) {
                userData.setEventResetTime(DateHelper.getTodayZeroTimeStamp() + config.getNpcEventBeginTime());
                userData.setEventTodayCount(0);
            }
        }

        Map<Long, Map<Long, Map<Integer, Integer>>> serverSyncMap = new ConcurrentHashMap<>();
        if (nowTime >= npcEventAddTime + config.getNpcEventInterval()) {
            long times = (nowTime - npcEventAddTime) / config.getNpcEventInterval();
            npcEventAddTime = npcEventAddTime + times * config.getNpcEventInterval();
            for (int i = 0; i < times; i++) {
                for (ChannelTonBingUserData userData : userDataMap.values()) {
                    Map<Integer, Integer> userEventMap = userData.getEventMap();
                    if (userEventMap.size() < config.getNpcEventMaxNum() && userData.getEventTodayCount() < config.getNpcEventDayMaxNum()) {
                        int eventPos = randomNpcEventPosition(userEventMap);
                        if (eventPos != -1) {
                            ChannelTonBingNpcEventConfig npcEventConfig = randomNpcEvent();
                            userEventMap.put(eventPos, npcEventConfig.getId());
                            userData.setUpdateOption();
                            userData.setEventTodayCount(userData.getEventTodayCount() + 1);
                            UserBaseInfo userBaseInfo = ChannelUserMgr.getUserBaseInfo(userData.getUserId());
                            if (userBaseInfo != null) {
                                if (!serverSyncMap.containsKey(userBaseInfo.getServerId())) {
                                    serverSyncMap.put(userBaseInfo.getServerId(), new ConcurrentHashMap<>());
                                }
                                Map<Long, Map<Integer, Integer>> userSyncMap = serverSyncMap.get(userBaseInfo.getServerId());
                                if (!userSyncMap.containsKey(userData.getUserId())) {
                                    userSyncMap.put(userData.getUserId(), new ConcurrentHashMap<>());
                                }
                                userSyncMap.get(userData.getUserId()).put(eventPos, npcEventConfig.getId());
                            }
                            // 加红点
                            addRedDot(userData, eTonBingRedDotType.Type1_NpcEvent);
                            // 日志
                            ChannelAutoLogMgr.add(new LogChannelTonBingNpcEvent(
                                    config.getActivityInfo().getActivityId(),
                                    userData.getUserId(),
                                    1,
                                    npcEventConfig.getId(),
                                    -1,
                                    "-1",
                                    channelTonBingData.getMatchId(),
                                    channelTonBingData.getTurn(),
                                    channelTonBingData.getWarType()
                            ));
                        }
                    }
                }
            }
        }
        if (serverSyncMap.size() > 0) {
            syncServerNpcEvent(serverSyncMap);
        }
        getChannelTonBingData().setNpcEventAddTime(npcEventAddTime);
    }

    //随机npc事件位置
    private int randomNpcEventPosition(Map<Integer, Integer> userEventMap) {
        List<Integer> eventPosList = new ArrayList<>();
        int npcEventPositionNum = config.getNpcEventPositionNum();
        if (userEventMap.size() >= npcEventPositionNum) {
            return -1;
        }
        //十个位置就是0到9
        for (int i = 0; i < npcEventPositionNum; i++) {
            if (!userEventMap.containsKey(i)) {
                eventPosList.add(i);
            }
        }
        int randomValue = randomHelper.next(0, eventPosList.size());
        return eventPosList.get(randomValue);
    }

    private ChannelTonBingNpcEventConfig randomNpcEvent() {
        Map<Integer, ChannelTonBingNpcEventConfig> eventConfigMap = config.getNpcEventConfigMap();
        ChannelTonBingNpcEventConfig randomNpcEventConfig = null;
        int totalWeight = 0;
        for (ChannelTonBingNpcEventConfig npcEventConfig : eventConfigMap.values()) {
            int weight = npcEventConfig.getWeight();
            totalWeight += weight;
        }
        int randomNum = randomHelper.next(0, totalWeight);
        for (ChannelTonBingNpcEventConfig npcEventConfig : eventConfigMap.values()) {
            int value = npcEventConfig.getWeight();
            if (randomNum < value) {
                randomNpcEventConfig = npcEventConfig;
                break;
            }
            randomNum -= value;
        }
        return randomNpcEventConfig;
    }

    //处理事件
    public void dealEvent(long userId, ChannelTonBingProto.ChannelTonBingDealEventReqMsg reqMsg) {
        ChannelTonBingProto.CTonBingDealEventRespMsg.Builder respMsg = ChannelTonBingProto.CTonBingDealEventRespMsg.newBuilder();
        respMsg.setRet(0);
        ChannelTonBingUserData userData = getUserData(userId);
        if (!isAble(userData)) {
            respMsg.setRet(GameErrorCode.E_TON_BING_NO_ABLE);
            sendChannelPacket(ClientProtocol.U_CTON_BING_DEAL_EVENT, respMsg, userData);
            return;
        }
        synchronized (lock) {
            Map<Integer, Integer> eventMap = userData.getEventMap();
            if (!eventMap.containsKey(reqMsg.getPositionNo())) {
                respMsg.setRet(GameErrorCode.E_TON_BING_EVENT_POSITION);
                sendChannelPacket(ClientProtocol.U_CTON_BING_DEAL_EVENT, respMsg, userData);
                return;
            }
            int eventId = eventMap.get(reqMsg.getPositionNo());
            Map<Integer, ChannelTonBingNpcEventConfig> eventConfigMap = config.getNpcEventConfigMap();
            ChannelTonBingNpcEventConfig eventConfig = eventConfigMap.get(eventId);
            List<String> rewardList = eventConfig.getRewardList();
            String reward = "";
            int randomValue = -1;
            if (eventConfig.getType() == eTonBingNpcEventType.Option.getValue()) {
                if (reqMsg.getChoice() > rewardList.size() || reqMsg.getChoice() < 1) {
                    respMsg.setRet(GameErrorCode.E_TON_BING_EVENT_CHOICE);
                    sendChannelPacket(ClientProtocol.U_CTON_BING_DEAL_EVENT, respMsg, userData);
                    return;
                }
                reward = rewardList.get(reqMsg.getChoice() - 1);
            } else if (eventConfig.getType() == eTonBingNpcEventType.Dispatch.getValue()) {
                randomValue = randomHelper.next(0, rewardList.size());
                reward = rewardList.get(randomValue);
            } else if (eventConfig.getType() == eTonBingNpcEventType.Event.getValue()) {
                randomValue = randomHelper.next(0, rewardList.size());
                reward = rewardList.get(randomValue);
            }
            respMsg.setRandom(randomValue);
            respMsg.setReward(reward);
            eventMap.remove(reqMsg.getPositionNo());
            userData.setUpdateOption();
            sendChannelPacket(ClientProtocol.U_CTON_BING_DEAL_EVENT, respMsg, userData);

            // 发游戏服加奖励
            ChannelTonBingProto.ChannelTonBingAddRewardReqMsg.Builder builder = ChannelTonBingProto.ChannelTonBingAddRewardReqMsg.newBuilder();
            builder.setReward(reward);
            builder.setLogType1(eLogMoneyType.TonBing.getValue());
            builder.setLogType2(eLogMoneyType.TonBingNpcEventReward.getValue());
            sendChannelPacket(GameProtocol.S_TON_BING_ADD_REWARD, builder, userData);

            // 日志
            ChannelAutoLogMgr.add(new LogChannelTonBingNpcEvent(
                    config.getActivityInfo().getActivityId(),
                    userId,
                    2,
                    eventId,
                    eventConfig.getType() == eTonBingNpcEventType.Option.getValue() ? reqMsg.getChoice() : -1,
                    reward,
                    channelTonBingData.getMatchId(),
                    channelTonBingData.getTurn(),
                    channelTonBingData.getWarType()
            ));
        }
    }

    public void getBattleFieldByCmd(ChannelTonBingUserData userData,long userId, ChannelTonBingProto.CTonBingGetBattlefieldReqMsg reqMsg) {
        int ret = getBattleField(userData,userId, reqMsg);
        if (ret != 0) {
            ChannelTonBingProto.CTonBingGetBattlefieldRespMsg.Builder builder = ChannelTonBingProto.CTonBingGetBattlefieldRespMsg.newBuilder();
            builder.setRet(ret);
            sendChannelPacket(ClientProtocol.U_CTON_BING_GET_BATTLE_FIELD, builder, userData);
        }
    }

    public void getCityDetailByCmd(ChannelTonBingUserData userData, ChannelTonBingProto.CTonBingGetCityDetailReqMsg reqMsg) {
        int cityId = reqMsg.getCityId();
        ChannelTonBingCity city = getChannelTonBingData().getCityMap().get(cityId);
        if (city == null) {
            ChannelTonBingProto.CTonBingGetCityDetailRespMsg.Builder builder = ChannelTonBingProto.CTonBingGetCityDetailRespMsg.newBuilder();
            builder.setRet(GameErrorCode.E_TON_BING_PARAM_ERR);
            sendChannelPacket(ClientProtocol.U_CTON_BING_GET_CITY_DETAIL, builder, userData);
            return;
        }

        ChannelTonBingProto.CTonBingGetCityDetailRespMsg.Builder builder = ChannelTonBingProto.CTonBingGetCityDetailRespMsg.newBuilder();
        builder.setRet(0);
        builder.setCity(parseTonBingCityDetailMsg(city));
        sendChannelPacket(ClientProtocol.U_CTON_BING_GET_CITY_DETAIL, builder, userData);
    }

    public void getCityTeamByCmd(long userId, ChannelTonBingProto.CTonBingSyncCityTeamReqMsg reqMsg) {
        getCityTeam(userId, reqMsg);
    }

    public void useWarDrumByCmd(long userId, ChannelTonBingProto.ChannelTonBingUseWarDrumReqMsg reqMsg) {
        ChannelTonBingUserData userData = getUserData(userId);
        int ret = useWarDrum(userId, reqMsg.getReq());

        ChannelTonBingProto.CTonBingUseWarDrumRespMsg.Builder resp = ChannelTonBingProto.CTonBingUseWarDrumRespMsg.newBuilder();
        resp.setRet(ret);

        ChannelTonBingProto.ChannelTonBingUseWarDrumRespMsg.Builder builder = ChannelTonBingProto.ChannelTonBingUseWarDrumRespMsg.newBuilder();
        builder.setResp(resp);
        builder.setCost(reqMsg.getCost());
        sendChannelPacket(GameProtocol.S_CTON_BING_USE_WAR_DRUM_FROM_CHANNEL, builder, userData);
    }

    public void needHelpByCmd(long userId, ChannelTonBingProto.CTonBingNeedHelpReqMsg reqMsg) {
        ChannelTonBingUserData userData = getUserData(userId);
        int ret = needHelp(userId, reqMsg);

        ChannelTonBingProto.CTonBingNeedHelpRespMsg.Builder builder = ChannelTonBingProto.CTonBingNeedHelpRespMsg.newBuilder();
        builder.setRet(ret);
        if (ret == 0) {
            builder.setSyncNeedHelpTime(getUserData(userId).getNeedHelpTime());
        }
        sendChannelPacket(ClientProtocol.U_CTON_BING_NEED_HELP, builder, userData);
    }

    public void getBattlefieldRankByCmd(long userId, ChannelTonBingProto.CTonBingGetBattlefieldRankReqMsg reqMsg) {
        ChannelTonBingUserData userData = getUserData(userId);
        int cityId = reqMsg.getCityId();
        ChannelTonBingCity city = getChannelTonBingData().getCityMap().get(cityId);

        ChannelTonBingProto.CTonBingGetBattlefieldRankRespMsg.Builder builder = ChannelTonBingProto.CTonBingGetBattlefieldRankRespMsg.newBuilder();
        if (city == null) {
            builder.setRet(GameErrorCode.E_TON_BING_CITY_NO_EXIST);
            sendChannelPacket(ClientProtocol.U_CTON_BING_GET_BATTLEFIELD_RANK, builder, userData);
            return;
        }
        ChannelTonBingBattlefield battlefield = city.getBattlefield();
        if (battlefield == null) {
            builder.setRet(GameErrorCode.E_TON_BING_NO_BATTLE);
            sendChannelPacket(ClientProtocol.U_CTON_BING_GET_BATTLEFIELD_RANK, builder, userData);
            return;
        }

        builder.setRet(0);

        int rankNum = 0;
        int myRank = -1;
        long myValue = 0;
        for (ChannelTonBingBattlefieldRank rank : new LinkedList<>(battlefield.getBattlefieldRankNewList())) {
            rankNum++;
            if (rank.getUserId() == userId) {
                myValue = rank.getZhanGong();
                myRank = rankNum;
            }
            builder.addList(parseTonBingBattlefieldRankMsg(rank));
        }

        builder.setMyRank(myRank);
        builder.setMyValue(myValue);
        sendChannelPacket(ClientProtocol.U_CTON_BING_GET_BATTLEFIELD_RANK, builder, userData);
    }

    public void sendChatByCmd(long userId, CrossActivityProto.CrossActivityChatReqMsg.Builder reqMsg) {
        int range = 0;
        int id = 0;
        if (eMessageType.TonBingPublicChat.getValue() == reqMsg.getMsg().getType()) {
            range = ChannelTonBingChatLogic.RANGE_ALL;
            id = config.getActivityInfo().getActivityId();
            // 公屏聊天加上阵营
            WorldMessageProto.WorldMessageTempMsg.Builder message = reqMsg.getMsg().toBuilder();
            message.setExtraParams(String.valueOf(getUserData(userId).getCampId()));
            reqMsg.setMsg(message);
        } else if (eMessageType.TonBingCampChat.getValue() == reqMsg.getMsg().getType()) {
            range = ChannelTonBingChatLogic.RANGE_CAMP;
            id = getUserData(userId).getCampId();
        } else if (eMessageType.TonBingBattlefieldChat.getValue() == reqMsg.getMsg().getType()) {
            range = ChannelTonBingChatLogic.RANGE_BATTLEFIELD;
            UserScene userScene = ChannelSceneMgr.getUserScene(userId);
            if (userScene != null && eSceneType.TonBingBattlefield.getValue() == userScene.getSceneType()) {
                try {
                    String cityIdStr = userScene.getSceneId().split("-")[1];
                    id = Integer.valueOf(cityIdStr);
                } catch (Exception e) {
                }
            }
        }

        if (range <= 0 || id <= 0) {
            return;
        }

        chatLogic.chat(range, id, 1, reqMsg.getMsg(),channelTonBingData.getMatchId());
    }

    public List<WorldMessageProto.WorldMessageTempMsg>  getChatByCmd(ChannelTonBingUserData userData, Integer range, int param2) {
        List<WorldMessageProto.WorldMessageTempMsg> dataList = new ArrayList<>();
        int id = 0;
        if (range == ChannelTonBingChatLogic.RANGE_ALL) {
            id = config.getActivityInfo().getActivityId();
        } else if (range == ChannelTonBingChatLogic.RANGE_CAMP) {
            id = userData == null ? 0 : userData.getCampId();
        } else if (range == ChannelTonBingChatLogic.RANGE_BATTLEFIELD) {
            // 城池id
            id = param2;
        }

        if (range <= 0 || id <= 0) {
            return dataList;
        }
        dataList.addAll(chatLogic.getChatMsg(range, id));
        return dataList;
    }

    public void getRankRewardByCmd(long userId, ChannelTonBingProto.CTonBingGetRankRewardReqMsg reqMsg) {
        ChannelTonBingUserData userData = getUserData(userId);
        int ret = getRankReward(userId, reqMsg.getConditionType());
        if (ret != 0) {
            ChannelTonBingProto.CTonBingGetRankRewardRespMsg.Builder builder = ChannelTonBingProto.CTonBingGetRankRewardRespMsg.newBuilder();
            builder.setRet(ret);
            sendChannelPacket(ClientProtocol.U_CTON_BING_GET_RANK_REWARD, builder, userData);
        }
    }

    public void getCampRankListByCmd(long userId) {
        ChannelTonBingUserData userData = getUserData(userId);
        ChannelTonBingProto.CTonBingGetCampRankRespMsg.Builder builder = ChannelTonBingProto.CTonBingGetCampRankRespMsg.newBuilder();
        builder.setRet(0);

        for (ChannelTonBingRank rank : getCampScoreRankList()) {
            builder.addRank(parseTonBingCampRankMsg(rank));
        }

        sendChannelPacket(ClientProtocol.U_CTON_BING_GET_CAMP_RANK_LIST, builder, userData);
    }

    //获取战功和积分的总排名
    private Map<Integer,Map<Integer,Map<Integer,Long>>> getCampValueMap(){
        //matchId 1战功 2积分 campId 值
        Map<Integer,Map<Integer,Map<Integer,Long>>> mmMap = new ConcurrentHashMap<>();
        int matchId = channelTonBingData.getMatchId();
        mmMap.put(matchId,new ConcurrentHashMap<>());
        Map<Integer, Long> campIntegralMap = new ConcurrentHashMap<>();
        for (ChannelTonBingCity city : channelTonBingData.getCityMap().values()) {
            int campId = city.getCampId();
            int cityId = city.getCityId();
            if (campId == 0) {
                continue;
            }
            long addIntegral = config.getCityScore(channelTonBingData.getWarType(),cityId,channelTonBingData.getTurn());
            long oldIntegral = campIntegralMap.getOrDefault(campId, 0L);
            campIntegralMap.put(campId, oldIntegral + addIntegral);
        }
        mmMap.get(matchId).put(2,campIntegralMap);
        Map<Integer, Long> campZhanGongMap = new ConcurrentHashMap<>();
        for(ChannelTonBingUserData userData : getUserDataMap().values()){
            Map<Integer, Long> zgMap = userData.getZhanGongMap();
            long addZhangong = zgMap.getOrDefault(matchId,0L);
            long oldZhangong = campZhanGongMap.getOrDefault(userData.getCampId(), 0L);
            campZhanGongMap.put(userData.getCampId(), oldZhangong + addZhangong);
        }
        mmMap.get(matchId).put(1,campZhanGongMap);
        return mmMap;
    }

    public List<ChannelTonBingRank> getTodayCampUnionRank(int campId){
        List<ChannelTonBingRank> rankList = new ArrayList<>();
        List<ChannelTonBingRank> newRankList = new ArrayList<>();
        if (campId == 0) {
            // 获取全部商会
            for (ChannelTonBingRankListModel model : campUnionRankMap.values()) {
                if (model != null) {
                    rankList.addAll(model.getRankList());
                }
            }
        } else {
            ChannelTonBingRankListModel model = campUnionRankMap.get(campId);
            if (model != null) {
                rankList.addAll(model.getRankList());
            }
        }
        for(ChannelTonBingRank rank : rankList){
            //要的是今天的，要new一个新的额
            ChannelTonBingRank newRank = new ChannelTonBingRank();
            ChannelTonBingUnionJoin unionJoin = getUnionJoin(rank.getId());
            newRank.setId(rank.getId());
            newRank.setWarType(channelTonBingData.getWarType());
            newRank.setAbility(unionJoin.getTotalAbility());
            long value = 0L;
            for (ChannelTonBingUnionJoinMember member : unionJoin.getMemberMap().values()) {
                ChannelTonBingUserData tmp = getUserData(member.getUserId());
                value += tmp.getZhanGongMap().getOrDefault(channelTonBingData.getMatchId(),0L);
            }
            newRank.setValue(value);
            newRankList.add(newRank);
        }
        newRankList.sort(new ChannelTonBingRankSort());
        return newRankList;
    }

    public void getCampUnionRankListByCmd(long userId, ChannelTonBingProto.CTonBingGetCampUnionRankReqMsg reqMsg) {
        int matchId = reqMsg.getMatchId();
        int campId = reqMsg.getCampId();
        ChannelTonBingUserData userData = getUserData(userId);
        String unionUid = "";
        int myCampId = 0;
        if(userData != null){
            unionUid = userData.getUnionUid();
            myCampId = userData.getCampId();
        }

        ChannelTonBingProto.CTonBingGetCampUnionRankRespMsg.Builder builder = ChannelTonBingProto.CTonBingGetCampUnionRankRespMsg.newBuilder();
        builder.setRet(0);
        List<ChannelTonBingRank> rankList = getTodayCampUnionRank(campId);

        int rankShowLimit = unionMaxRankConfig;
        if (campId == 0) {
            rankShowLimit = unionMaxRankConfig * config.getCampNum();
        }
        // 获取商会排名
        int num = 0;
        for (ChannelTonBingRank rank : rankList) {
            num++;
            if (num > rankShowLimit) {
                break;
            }
            builder.addRank(parseTonBingCampUnionRankMsg(rank,channelTonBingData.getWarType()));
        }

        // 获取商会阵营排名
        int myRank = -1;
        if (myCampId != 0) {
            ChannelTonBingRankListModel model = campUnionRankMap.get(myCampId);
            if (model != null) {
                int cnt = 0;
                for (ChannelTonBingRank rank : model.getRankList()) {
                    cnt++;
                    if (cnt > rankShowLimit) {
                        break;
                    }
                    if (Objects.equals(rank.getId(), unionUid)) {
                        myRank = cnt;
                    }
                }
            }
        }
        builder.setMyRank(myRank);

        long myValue = 0L;
        ChannelTonBingUnionJoin unionJoin = getUnionJoin(unionUid);
        if (unionJoin != null) {
            for (ChannelTonBingUnionJoinMember member : unionJoin.getMemberMap().values()) {
                ChannelTonBingUserData tmp = getUserData(member.getUserId());
                myValue += tmp.getZhanGongMap().getOrDefault(matchId,0L);
            }
        }
        builder.setMyValue(myValue);
        sendChannelPacket(ClientProtocol.U_CTON_BING_GET_CAMP_UNION_RANK_LIST, builder, userData);
    }

    public ChannelTonBingProto.CTonBingTodayRankTempMsg.Builder getCampUnionRankToday(long userId) {
        String unionUid = "";
        int myCampId = 0;
        if(containsUser(userId)){
            ChannelTonBingUserData userData = getUserData(userId);
            //不在这场比赛
            unionUid = userData.getUnionUid();
            myCampId = userData.getCampId();
        }

        ChannelTonBingProto.CTonBingTodayRankTempMsg.Builder builder = ChannelTonBingProto.CTonBingTodayRankTempMsg.newBuilder();
        builder.setWarType(channelTonBingData.getWarType());
        builder.setMatchId(channelTonBingData.getMatchId());
        List<ChannelTonBingRank> rankList = getTodayCampUnionRank(0);

        int rankShowLimit = unionMaxRankConfig;
        rankShowLimit = unionMaxRankConfig * config.getCampNum();
        // 获取商会排名
        int num = 0;
        for (ChannelTonBingRank rank : rankList) {
            num++;
            if (num > rankShowLimit) {
                break;
            }
            builder.addRank(parseTonBingCampUnionRankMsg(rank,channelTonBingData.getWarType()));
        }

        // 获取商会阵营排名
        int myRank = -1;
        if (myCampId != 0) {
            ChannelTonBingRankListModel model = campUnionRankMap.get(myCampId);
            if (model != null) {
                int cnt = 0;
                for (ChannelTonBingRank rank : model.getRankList()) {
                    cnt++;
                    if (cnt > rankShowLimit) {
                        break;
                    }
                    if (Objects.equals(rank.getId(), unionUid)) {
                        myRank = cnt;
                    }
                }
            }
        }
        builder.setMyRank(myRank);

        long myValue = 0L;
        ChannelTonBingUnionJoin unionJoin = getUnionJoin(unionUid);
        if (unionJoin != null) {
            for (ChannelTonBingUnionJoinMember member : unionJoin.getMemberMap().values()) {
                ChannelTonBingUserData tmp = getUserData(member.getUserId());
                myValue += tmp.getZhanGongMap().getOrDefault(channelTonBingData.getMatchId(),0L);
            }
        }
        builder.setMyValue(myValue);

        return builder;
    }



    public void getUserRankListByCmd(long userId, ChannelTonBingProto.CTonBingGetUserRankReqMsg reqMsg) {
        int type = reqMsg.getType();

        long myValue = getUserData(userId).getZhanGong();
        String myId = String.valueOf(userId);
        LinkedList<ChannelTonBingRank> rankList;
        int maxRank = 100;

        // 【查其他玩家排名】
        if (type == 3) {
            getOtherUserRankByCmd(userId, reqMsg.getParam());
            return; // ！！！
        }

        if (type == 1) {
            // 【个人榜】
            rankList = userRank.getRankList();
            maxRank = userMaxRankConfig;
        } else if (type == 2) {
            // 【商会内个人榜】
            String unionUid = reqMsg.getParam();
            ChannelTonBingUnionJoin unionJoin = getUnionJoin(unionUid);
            if (StringUtils.isNullOrEmpty(unionUid) || unionJoin == null) {
                return; // 参数错误
            }
            // 组装个榜单
            ChannelTonBingRankListModel tmpModel = new ChannelTonBingRankListModel();
            LinkedList<ChannelTonBingRank> tmpList = new LinkedList<>();
            for (Long memberId : unionJoin.getMemberMap().keySet()) {
                ChannelTonBingUserData member = getUserData(memberId);
                tmpList.add(new ChannelTonBingRank(String.valueOf(member.getUserId()), member.getZhanGong(), 0L));
            }
            tmpModel.setRankList(tmpList); // 排序
            rankList = tmpModel.getRankList();
        } else {
            return; // 参数错误
        }

        ChannelTonBingProto.CTonBingGetUserRankRespMsg.Builder builder = ChannelTonBingProto.CTonBingGetUserRankRespMsg.newBuilder();
        builder.setRet(0);

        int myRank = -1;
        int rankNum = 0;
        for (ChannelTonBingRank rank : rankList) {
            rankNum++;
            if (rankNum > maxRank) {
                break;
            }
            if (Objects.equals(rank.getId(), myId)) {
                myRank = rankNum;
            }
            builder.addRank(parseTonBingUserRankMsg(rank));
        }

        builder.setMyRank(myRank);
        builder.setMyValue(myValue);
        ChannelTonBingUserData userData = getUserData(userId);
        sendChannelPacket(ClientProtocol.U_CTON_BING_GET_USER_RANK_LIST, builder, userData);
    }

    public void getOtherUserRankByCmd(long userId, String otherUserId) {
        ChannelTonBingUserData userData = getUserData(userId);

        int otherRank = -1;
        long otherValue = 0L;

        int num = 0;
        for (ChannelTonBingRank rank : userRank.getRankList()) {
            num++;
            if (num > userMaxRankConfig) {
                break;
            }
            if (Objects.equals(rank.getId(), otherUserId)) {
                otherRank = num;
                otherValue = rank.getValue();
                break;
            }
        }

        ChannelTonBingProto.CTonBingGetUserRankRespMsg.Builder builder = ChannelTonBingProto.CTonBingGetUserRankRespMsg.newBuilder();
        builder.setRet(0);
        builder.setMyRank(otherRank);
        builder.setMyValue(otherValue);
        // builder.setRank(); // 传排名，不需要传rankList
        sendChannelPacket(ClientProtocol.U_CTON_BING_GET_USER_RANK_LIST, builder, userData);
    }

    // ============================== SYNC ==============================

    public void syncBaseDataMsg(ChannelTonBingUserData userData) {
        if (userData == null) {
            return;
        }

        int joinStatus = 0; // 0未报名、1已报名、2有资格、3商会未入选
        if (getChannelTonBingData().isLockUnion()) {
            joinStatus = isAble(userData) ? 2 : 3;
        } else {
            for (ChannelTonBingUnionJoin join : unionJoinMap.values()) {
                if (join.getMemberMap().containsKey(userData.getUserId())) {
                    joinStatus = 1;
                    break;
                }
            }
        }

        ChannelTonBingProto.CTonBingSyncBaseDataRespMsg.Builder builder = ChannelTonBingProto.CTonBingSyncBaseDataRespMsg.newBuilder();
        builder.setJoinStatus(joinStatus);
        if (userData.getCampId() != 0) {
            builder.setCampId(userData.getCampId());
        }
        builder.setSyncPatronsTime(userData.getLastSyncPatronsTime());
        builder.setGuide(userData.getGuide());
        builder.setSyncNeedHelpTime(userData.getNeedHelpTime());
        sendChannelPacket(ClientProtocol.U_CTON_BING_SYNC_BASE_DATA, builder, userData);
    }

    /**
     * 跨服同步用户数据到游戏服
     */
    public void syncUserDataToGameServer(ChannelTonBingUserData userData) {
        if (userData == null) {
            return;
        }
        ChannelTonBingProto.ChannelTonBingSyncUserDataMsg.Builder builder = ChannelTonBingProto.ChannelTonBingSyncUserDataMsg.newBuilder();
        builder.setCampId(userData.getCampId());
        builder.setZhanGong(userData.getZhanGong());
        builder.setSendLogin(false);
        sendChannelPacket(GameProtocol.S_CHANNEL_CTON_BING_SYNC_USER_DATA, builder, userData);
    }

    public void syncTeamEnterBattlefield(ChannelTonBingCity city, ChannelTonBingBattlefield battlefield, ChannelTonBingBattlefieldPart part) {

        // 获取场景内玩家
        List<Long> userIds = getBattlefieldUserIdList(city.getCityId(), part.getPartId());

        ChannelTonBingProto.CTonBingSyncTeamEnterRespMsg.Builder builder = ChannelTonBingProto.CTonBingSyncTeamEnterRespMsg.newBuilder();
        builder.setBattlefield(parseTonBingBattlefieldMsg(part, battlefield, city));

        sendChannelPacketToAll(ClientProtocol.U_CTON_BING_SYNC_TEAM_ENTER, builder, userIds);
    }

    public void syncUseWarDrum(ChannelTonBingCity city, ChannelTonBingBattlefield battlefield, ChannelTonBingBattlefieldPart part) {

        // 获取场景内玩家
        List<Long> userIds = getBattlefieldUserIdList(city.getCityId(), part.getPartId());

        ChannelTonBingProto.CTonBingSyncUseDrumRespMsg.Builder builder = ChannelTonBingProto.CTonBingSyncUseDrumRespMsg.newBuilder();
        builder.setBattlefield(parseTonBingBattlefieldMsg(part, battlefield, city));

        sendChannelPacketToAll(ClientProtocol.U_CTON_BING_SYNC_WAR_DRUM, builder, userIds);
    }

    public void syncBattleResultMsg(List<ChannelTonBingBattleResult> resultList, ChannelTonBingCityWarReport report) {
        for (ChannelTonBingBattleResult result : resultList) {

            // 获取战场内玩家
            List<Long> userIds = new ArrayList<>();
            List<Long> fuserIds = getBattlefieldUserIdList(result.getCity().getCityId(), result.getPart().getPartId());
            for(long fuserId : fuserIds){
                if(watchUserIdList.contains(fuserId)){
                    userIds.add(fuserId);
                }
            }
            if (userIds.isEmpty()) {
                continue; // 没人在里面，不同步了
            }


            // 战场数据
            ChannelTonBingProto.CTonBingBattlefieldMsg.Builder battlefieldMsg = parseTonBingBattlefieldMsg(result.getPart(), result.getBattlefield(), result.getCity());

            // 战斗结果数据
            ChannelTonBingProto.CTonBingBattleResultMsg.Builder battleResultMsg = ChannelTonBingProto.CTonBingBattleResultMsg.newBuilder();
            battleResultMsg.setDefWin(result.getWinType() == 1);
            battleResultMsg.setDefHpReduce(result.getDefHpReduce());
            battleResultMsg.setAtkHpReduce(result.getAtkHpReduce());
            battleResultMsg.setBattlefield(battlefieldMsg);
            if (report != null) {
                battleResultMsg.setResult(parseTonBingCityWarReportMsg(report));
            }
            battleResultMsg.setWinTimes(result.getWinTimes());

            // 返回值
            ChannelTonBingProto.CTonBingSyncBattleResultMsg.Builder builder = ChannelTonBingProto.CTonBingSyncBattleResultMsg.newBuilder();
            builder.setBattle(battleResultMsg);

            sendChannelPacketToAll(ClientProtocol.U_CTON_BING_BATTLE_RESULT, builder, userIds);
        }
    }

    public void syncBarracksMsg(long userId) {
        ChannelTonBingUserData userData = getUserData(userId);
        if (isInActivity(userData, System.currentTimeMillis())) {
            ChannelTonBingProto.CTonBingSyncBarracksRespMsg.Builder respMsg = ChannelTonBingProto.CTonBingSyncBarracksRespMsg.newBuilder();
            respMsg.setBarracks(parseTonBingBarracksMsg(userId));
            sendChannelPacket(ClientProtocol.U_CTON_BING_SYNC_BARRACKS, respMsg, userData);
        }
    }

    public void syncCityTeamToSelf(ChannelTonBingCity city) {
        // 拿所有队伍
        List<ChannelTonBingTeam> teamList = new ArrayList<>();
        for (ChannelTonBingTeamArrive teamArrive : new LinkedList<>(city.getAtkTeamList())) {
            teamList.add(getTeamByArrive(teamArrive));
        }
        for (ChannelTonBingTeamArrive teamArrive : new LinkedList<>(city.getDefTeamList())) {
            teamList.add(getTeamByArrive(teamArrive));
        }
        ChannelTonBingBattlefield battlefield = city.getBattlefield();
        if (battlefield != null) {
            for (ChannelTonBingBattlefieldPart part : battlefield.getPartMap().values()) {
                for (ChannelTonBingBattlefieldTeam tmp : new LinkedList<>(part.getAtkList())) {
                    teamList.add(getTeamByArrive(tmp.getTeam()));
                }
                for (ChannelTonBingBattlefieldTeam tmp : new LinkedList<>(part.getDefList())) {
                    if (tmp.getTeam() != null) {
                        teamList.add(getTeamByArrive(tmp.getTeam()));
                    }
                }
            }
        }

        // 按玩家分类
        Map<Long, List<ChannelTonBingTeam>> userTeamMap = new ConcurrentHashMap<>();
        for (ChannelTonBingTeam team : teamList) {
            userTeamMap.putIfAbsent(team.getUserId(), new ArrayList<>());
            userTeamMap.get(team.getUserId()).add(team);
        }

        // 按玩家同步
        for (Map.Entry<Long, List<ChannelTonBingTeam>> entry : userTeamMap.entrySet()) {
            syncTeamToSelf(entry.getKey(), entry.getValue());
        }
    }

    public void syncTeamToSelf(long userId, List<ChannelTonBingTeam> teamList) {
        ChannelTonBingUserData userData = getUserData(userId);
        long now = System.currentTimeMillis();
        if (!isInActivity(getUserData(userId), now)) {
            return;
        }

        ChannelTonBingProto.CTonBingSyncTeamRespMsg.Builder builder = ChannelTonBingProto.CTonBingSyncTeamRespMsg.newBuilder();
        for (ChannelTonBingTeam team : teamList) {
            builder.addTeam(parseTonBingTeamMsg(getUserData(team.getUserId()), team));
        }
        //自己在场景才同步，不然同步过去我自己不再这个战场去别的战场会窜台
        if(watchUserIdList.contains(userId)){
            sendChannelPacket(ClientProtocol.U_CTON_BING_SYNC_TEAM, builder, userData);
        }
    }

    /**
     * 同步发光数据
     */
    private void syncLight() {
        List<Long> userIdList = new ArrayList<>();
        for (ChannelTonBingRank rank : userRank.getRankList()) {
            if (userIdList.size() >= config.getLightRankNum() || rank.getValue() < config.getLightNeedZhanGong()) {
                break;
            }
            userIdList.add(Long.valueOf(rank.getId()));
        }

        // 获取在活动内的玩家
        long now = System.currentTimeMillis();
//        List<Long> recUsers = userDataMap.values().stream().filter(item -> isInActivity(item, now)).map(ChannelTonBingUserData::getUserId).collect(Collectors.toList());
        List<Long> recUsers = new ArrayList<>(watchUserIdList);
        if (recUsers.isEmpty()) {
            return;
        }

        ChannelTonBingProto.CTonBingSyncLightUserRespMsg.Builder builder = ChannelTonBingProto.CTonBingSyncLightUserRespMsg.newBuilder();
        builder.addAllUserId(userIdList);
        sendChannelPacketToAll(ClientProtocol.U_CTON_BING_SYNC_LIGHT_USER, builder, recUsers);
    }

    private void syncCityEventToAll() {
        ChannelTonBingProto.SyncCTonBingCityEventRespMsg.Builder builder = ChannelTonBingProto.SyncCTonBingCityEventRespMsg.newBuilder();
        for (ChannelTonBingCityEvent cityEvent : getChannelTonBingData().getCityEventMap().values()) {
            builder.addEvent(parseTonBingCityEventMsg(cityEvent));
        }
        sendChannelPacketToAll(ClientProtocol.U_CTON_BING_SYNC_CITY_EVENT, builder, userDataMap.keySet());
    }

    private void syncBuffToAll() {
        for (Map.Entry<Integer, List<ChannelTonBingCampBuff>> entry : getChannelTonBingData().getCampBuffMap().entrySet()) {
            Integer campId = entry.getKey();
            ChannelTonBingProto.CTonBingSyncBuffRespMsg.Builder builder = ChannelTonBingProto.CTonBingSyncBuffRespMsg.newBuilder();
            for (ChannelTonBingCampBuff buff : new ArrayList<>(entry.getValue())) {
                builder.addBuff(parseTonBingBuffMsg(buff));
            }
            ChannelTonBingBuffTypeConfig rankBuffConfig = getRankBuffInfo(campId);
            if(rankBuffConfig != null){
                ChannelTonBingCampBuff rankBuff = new ChannelTonBingCampBuff();
                rankBuff.setBuffId(rankBuffConfig.getId());
                rankBuff.setCampId(campId);
                rankBuff.setCityId(0);
                builder.addBuff(parseTonBingBuffMsg(rankBuff));
            }
            List<Long> campUserList = userDataMap.values().stream().filter(item -> item.getCampId() == campId).mapToLong(ChannelTonBingUserData::getUserId).boxed().collect(Collectors.toList());
//            List<Long> campUserList = new ArrayList<>(watchUserIdList);
            sendChannelPacketToAll(ClientProtocol.U_CTON_BING_SYNC_BUFF, builder, campUserList);
        }
    }

    /**
     * 同步指挥官信息给阵营所有玩家
     *
     * @param campId
     */
    private void syncCommanderInfoToCamp(int campId) {
        if (campId == 0) {
            return;
        }
        ChannelTonBingProto.CTonBingCommanderDataSyncMsg.Builder syncMsg = parseCommanderInfoMsg(campId);
        // 同步给阵营所有玩家
        List<Long> campUserList = userDataMap.values().stream().filter(item -> item.getCampId() == campId).mapToLong(ChannelTonBingUserData::getUserId).boxed().collect(Collectors.toList());
        sendChannelPacketToAll(ClientProtocol.U_CTON_BING_COMMANDER_DATA_SYNC, syncMsg, campUserList);
    }

    /**
     * 同步指挥官信息
     */
    public void syncCommanderInfo(long userId, int campId) {
        ChannelTonBingUserData userData = getUserData(userId);
        if (userData == null) {
            return;
        }
        if (campId == 0) {
            return;
        }
        ChannelTonBingProto.CTonBingCommanderDataSyncMsg.Builder syncMsg = parseCommanderInfoMsg(campId);
        sendChannelPacket(ClientProtocol.U_CTON_BING_COMMANDER_DATA_SYNC, syncMsg, userData);
    }

    private ChannelTonBingProto.CTonBingCommanderDataSyncMsg.Builder parseCommanderInfoMsg(int campId) {
        ChannelTonBingProto.CTonBingCommanderDataSyncMsg.Builder syncMsg = ChannelTonBingProto.CTonBingCommanderDataSyncMsg.newBuilder();
        ChannelTonBingProto.CTonBingCommanderDataTemp.Builder commanderDataMsg = ChannelTonBingProto.CTonBingCommanderDataTemp.newBuilder();
        ChannelTonBingCampMatchData channelTonBingCampData = campMatchDataMap.get(campId);
        ChannelTonBingCommanderData commanderData = channelTonBingCampData.getCommanderData();
        if (commanderData != null) {
            commanderDataMsg.setUserId(commanderData.getUserId());
            UserBaseInfo userBaseInfo = ChannelUserMgr.getUserBaseInfo(commanderData.getUserId());
            if (userBaseInfo != null) {
                commanderDataMsg.setPlayerBaseData(PlayerBasePb.parsePlayerBaseTempMsg(userBaseInfo));
            }
            commanderDataMsg.setNotice(commanderData.getNotice());
            commanderDataMsg.setMajorCityId(commanderData.getMajorCityId());
            commanderDataMsg.setMinorCityId(commanderData.getMinorCityId());
            syncMsg.setCommanderData(commanderDataMsg);
        }
        return syncMsg;
    }

    // ============================== PB ==============================

    public ChannelTonBingProto.CTonBingBuffMsg.Builder parseTonBingBuffMsg(ChannelTonBingCampBuff buff) {
        ChannelTonBingProto.CTonBingBuffMsg.Builder builder = ChannelTonBingProto.CTonBingBuffMsg.newBuilder();
        builder.setBuffId(buff.getBuffId());
        builder.setCityId(buff.getCityId());
        builder.setEndTime(getBuffEndTime(buff));
        return builder;
    }

    public ChannelTonBingProto.CTonBingCityEventMsg.Builder parseTonBingCityEventMsg(ChannelTonBingCityEvent cityEvent) {
        ChannelTonBingProto.CTonBingCityEventMsg.Builder builder = ChannelTonBingProto.CTonBingCityEventMsg.newBuilder();
        builder.setCityId(cityEvent.getCityId());
        builder.setBuffId(cityEvent.getBuffId());
        builder.setRefreshTime(cityEvent.getRefreshTime());
        if (cityEvent.getEffectCamp() != 0) {
            builder.setEffectCampId(cityEvent.getEffectCamp());
            builder.setEndTime(cityEvent.getEndTime());
        }
        return builder;
    }
    private ChannelTonBingProto.ChannelTonBingCityWinerAppointRespMsg.Builder parsePosAppoint(ChannelTonBingProto.ChannelTonBingCityWinerAppointRespMsg.Builder msg, Map<Integer, Integer> positionMap){
        Map<Integer, Integer> posMap = new ConcurrentHashMap(positionMap);
        for(int type : posMap.keySet()){
            ChannelTonBingProto.CTonBingPositionMsg.Builder posMsg = ChannelTonBingProto.CTonBingPositionMsg.newBuilder();
            posMsg.setType(type);
            posMsg.setPositionId(posMap.get(type));
            msg.addPosition(posMsg);
        }
        return msg;
    }

    public ChannelTonBingProto.CTonBingTeamMsg.Builder parseTonBingTeamMsg(ChannelTonBingUserData userData, ChannelTonBingTeam team) {
        // 获取主将
        int patrons1 = 0;
        int skin1 = 0;
        if (!team.getPatronsList().isEmpty()) {
            patrons1 = team.getPatronsList().get(0);
            ChannelTonBingPatrons patrons = userData.getPatronsMap().get(patrons1);
            if (patrons != null) {
                skin1 = patrons.getSkin();
            }
        }

        ChannelTonBingProto.CTonBingTeamMsg.Builder builder = ChannelTonBingProto.CTonBingTeamMsg.newBuilder();
        builder.setOccupation(team.getOccupation());
        builder.setPatronsId(patrons1);
        builder.setTeamStatus(getTeamStatus(team).getValue());
        builder.setCityId(team.getCityId());
        builder.addAllToCityId(new ArrayList<>(team.getRouteList()));
        builder.setStartTime(team.getBeginMoveTime());
        builder.setNextCityArriveTime(team.getArriveTime());
        builder.setLastCityDepartureTime(team.getSmallMoveTime());
        builder.setMaxSoldier(team.getMaxSoldier());
        builder.setSoldierNum(team.getSoldierNum());
        builder.setUser(parseTonBingUserMsg(userData.getUserId()));
        builder.setSkinId(skin1);
        builder.setHorseId(userData.getHorseId());
        builder.setTargetCityId(team.getTargetCityId());
        return builder;
    }

    public ChannelTonBingProto.CTonBingCityMsg.Builder parseTonBingCityMsg(ChannelTonBingCity city) {
        // 准备几个参数
        Set<Integer> atkCampId = new HashSet<>();
        int atkNum = 0;
        int defNum = 0;
        long helpNum = 0;

        ChannelTonBingBattlefield battlefield = city.getBattlefield();
        if (battlefield != null) {
            helpNum = battlefield.getHelpNum();
            for (ChannelTonBingBattlefieldPart part : battlefield.getPartMap().values()) {
                // 进攻、防守方数量
                atkNum += part.getAtkList().size();
                defNum += part.getDefList().size();
                if (atkCampId.size() < config.getCampNum() - 1) {
                    // 遍历攻击方找攻击方阵营
                    for (ChannelTonBingBattlefieldTeam atkTempTeam : new LinkedList<>(part.getAtkList())) {
                        ChannelTonBingTeamArrive arrive = atkTempTeam.getTeam();
                        if (arrive != null) {
                            atkCampId.add(getUserData(arrive.getUserId()).getCampId());
                        }
                    }
                }
            }
        }

        atkNum += city.getAtkTeamList().size();
        if (atkCampId.size() < config.getCampNum() - 1) {
            for (ChannelTonBingTeamArrive arrive : new LinkedList<>(city.getAtkTeamList())) {
                atkCampId.add(getUserData(arrive.getUserId()).getCampId());
            }
        }

        defNum = defNum + city.getDefTeamList().size() + getSoldierTeamCount(city);

        ChannelTonBingProto.CTonBingCityMsg.Builder builder = ChannelTonBingProto.CTonBingCityMsg.newBuilder();
        builder.setCityId(city.getCityId());
        builder.setCampId(city.getCampId());
        builder.setDefTeamNum(defNum);
        builder.setAtkTeamNum(atkNum);
        builder.setNeedHelpNum(helpNum);
        builder.addAllAtkCampId(new ArrayList<>(atkCampId));
        builder.setTeamRecTime(city.getTeamRecoveryBeginTime() == 0 ? 0 : city.getTeamRecoveryBeginTime() + config.getCityTeamRecoveryTime());
        return builder;
    }

    public ChannelTonBingProto.CTonBingCityDetailMsg.Builder parseTonBingCityDetailMsg(ChannelTonBingCity city) {

        ChannelTonBingCityConfig cityConfig = config.getCityConfigMap(channelTonBingData.getWarType()).get(city.getCityId());
        ChannelTonBingCityTypeConfig typeConfig = config.getCityTypeConfigMap(channelTonBingData.getWarType()).get(cityConfig.getType());

        Map<Long, ChannelTonBingDefUser> defUserMap = new HashMap<>();
        int defTeamNum = city.getTeamNum(); // 城防军数量
        int extraTeamNum = city.getExtraTeamNum(); // 额外的城防军数量
        int defUserTeamNum = 0; // 玩家队伍数量

        // 战场内拿防守队伍
        ChannelTonBingBattlefield battlefield = city.getBattlefield();
        if (battlefield != null) {
            for (ChannelTonBingBattlefieldPart part : battlefield.getPartMap().values()) {
                for (ChannelTonBingBattlefieldTeam battlefieldTeam : new LinkedList<>(part.getDefList())) {
                    if (battlefieldTeam.getTeam() == null) {
                        if (battlefieldTeam.getSoldierType() == eTonBingSoldierType.COMMON_SOLDIER.getValue()) {
                            defTeamNum++;
                        } else if (battlefieldTeam.getSoldierType() == eTonBingSoldierType.EXTRA_SOLDIER.getValue()) {
                            extraTeamNum++;
                        }
                    } else {
                        ChannelTonBingTeam team = getTeamByArrive(battlefieldTeam.getTeam());
                        if (!defUserMap.containsKey(team.getUserId())) {
                            defUserMap.put(team.getUserId(), new ChannelTonBingDefUser(team.getUserId()));
                        }
                        ChannelTonBingDefUser defUser = defUserMap.get(team.getUserId());
                        defUser.setTeamNum(defUser.getTeamNum() + 1);
                        defUser.setSoldierNum(defUser.getSoldierNum() + team.getSoldierNum());
                    }
                }
            }
        }
        // 排队里拿防守队伍
        for (ChannelTonBingTeamArrive arrive : new LinkedList<>(city.getDefTeamList())) {
            ChannelTonBingTeam team = getTeamByArrive(arrive);
            if (!defUserMap.containsKey(team.getUserId())) {
                defUserMap.put(team.getUserId(), new ChannelTonBingDefUser(team.getUserId()));
            }
            ChannelTonBingDefUser defUser = defUserMap.get(team.getUserId());
            defUser.setTeamNum(defUser.getTeamNum() + 1);
            defUser.setSoldierNum(defUser.getSoldierNum() + team.getSoldierNum());
        }

        // 计算防守玩家队伍数
        for (ChannelTonBingDefUser defUser : defUserMap.values()) {
            defUserTeamNum += defUser.getTeamNum();
        }

        // 是否当前阵营的出生城池
        boolean isBirthCity = cityConfig.getType() == 1 && cityConfig.getInitialCamp() == city.getCampId();

        // 防守队伍设置个显示上限
        List<ChannelTonBingDefUser> sortDefUserList = new ArrayList<>();
        if (!isBirthCity) {
            sortDefUserList = defUserMap.values().stream().sorted(Comparator.comparing(ChannelTonBingDefUser::getSoldierNum).reversed())
                    .limit(50).collect(Collectors.toList()); // 写死50个
        }

        // 返回值
        ChannelTonBingProto.CTonBingCityDetailMsg.Builder builder = ChannelTonBingProto.CTonBingCityDetailMsg.newBuilder();
        builder.setDefTeamNum(defTeamNum);
        builder.setExtraDefTeamNum(extraTeamNum);
        builder.setDefTeamSoldierNum(city.getTeamSoldierNum());
        builder.setFoodProduction(typeConfig.getFoodProduction());
        for (ChannelTonBingDefUser tmpDefTeam : sortDefUserList) {
            UserBaseInfo userBaseInfo = ChannelUserMgr.getUserBaseInfo(tmpDefTeam.getUserId());
            String nickname = userBaseInfo == null ? "" : userBaseInfo.getNickName();

            ChannelTonBingProto.CTonBingCityDefUserMsg.Builder tmp = ChannelTonBingProto.CTonBingCityDefUserMsg.newBuilder();
            tmp.setUserId(tmpDefTeam.getUserId());
            tmp.setName(nickname);
            tmp.setTeamNum(tmpDefTeam.getTeamNum());
            tmp.setTotalValue(tmpDefTeam.getSoldierNum());

            builder.addDefUser(tmp);
        }
        builder.setTotalTeamNum(defTeamNum + extraTeamNum + defUserTeamNum);
        return builder;
    }

    public ChannelTonBingProto.CTonBingBarracksMsg.Builder parseTonBingBarracksMsg(long userId) {
        ChannelTonBingProto.CTonBingBarracksMsg.Builder builder = ChannelTonBingProto.CTonBingBarracksMsg.newBuilder();
        ChannelTonBingUserData userData = getUserData(userId);
        ChannelTonBingBarracks barracks = userData.getBarracks();
        builder.setReadyNum(barracks.getReadyNum());
        builder.setReadyMaxNum(barracks.getReadyMaxNum());
        builder.setCreateNum(barracks.getCreateNum());
        builder.setCreateTime(barracks.getCreateTime());
        builder.setCreateSpeed(barracks.getCreateSpeed() == 0 ? 0 : DateHelper.SECOND_MILLIONS / barracks.getCreateSpeed());
        builder.setCreateCount(barracks.getCreateCount());
        builder.setWoundedNum(barracks.getWoundedNum());
        builder.setWoundedMaxNum(barracks.getHospitalSize());
        builder.setRescueNum(barracks.getRescueNum());
        builder.setRescueTime(barracks.getRescueTime());
        builder.setRescueSpeed(barracks.getRescueSpeed());
        builder.setRescueCount(barracks.getRescueCount());

        return builder;
    }

    public ChannelTonBingProto.CTonBingCityWarReportMsg.Builder parseTonBingCityWarReportMsg(ChannelTonBingCityWarReport report) {
        ChannelTonBingProto.CTonBingCityWarReportMsg.Builder builder = ChannelTonBingProto.CTonBingCityWarReportMsg.newBuilder();
        builder.addAllAtkCampId(report.getAtkCampId());
        builder.setDefCampId(report.getDefCampId());
        builder.setType(report.getType());
        builder.setCityId(report.getCityId());
        builder.setTime(report.getTime());
        for (ChannelTonBingBattlefieldRank rank : report.getAtkRankList()) {
            builder.addAtkRank(parseTonBingBattlefieldRankMsg(rank));
        }
        for (ChannelTonBingBattlefieldRank rank : report.getDefRankList()) {
            builder.addDefRank(parseTonBingBattlefieldRankMsg(rank));
        }
        builder.setWinCampId(report.getWinCampId());
        return builder;
    }

    public int getDefTeamNum(ChannelTonBingCity city, ChannelTonBingBattlefield battlefield, int type) {
        int num;
        if (type == eTonBingSoldierType.COMMON_SOLDIER.getValue()) {
            num = city.getTeamNum();
        } else if (type == eTonBingSoldierType.EXTRA_SOLDIER.getValue()) {
            num = city.getExtraTeamNum();
        } else {
            return 0;
        }
        for (ChannelTonBingBattlefieldPart channelTonBingBattlefieldPart : battlefield.getPartMap().values()) {
            // 统计守军数量
            for (ChannelTonBingBattlefieldTeam team : new LinkedList<>(channelTonBingBattlefieldPart.getDefList())) {
                if (team.getTeam() == null) {
                    if (team.getSoldierType() == type) {
                        num++;
                    }
                }
            }
        }
        return num;
    }

    public ChannelTonBingProto.CTonBingBattlefieldMsg.Builder parseTonBingBattlefieldMsg(ChannelTonBingBattlefieldPart part,
                                                                                    ChannelTonBingBattlefield battlefield,
                                                                                    ChannelTonBingCity city) {

        int cityDefTeamNum = city.getTeamNum();
        int cityExtraDefTeamNum = city.getExtraTeamNum();
        int defNum = city.getDefTeamList().size() + city.getTeamNum() + city.getExtraTeamNum();
        int atkNum = city.getAtkTeamList().size();
        for (ChannelTonBingBattlefieldPart channelTonBingBattlefieldPart : battlefield.getPartMap().values()) {
            defNum += channelTonBingBattlefieldPart.getDefList().size();
            atkNum += channelTonBingBattlefieldPart.getAtkList().size();
            // 统计守军数量
            for (ChannelTonBingBattlefieldTeam team : new LinkedList<>(channelTonBingBattlefieldPart.getDefList())) {
                if (team.getTeam() == null) {
                    if (team.getSoldierType() == eTonBingSoldierType.COMMON_SOLDIER.getValue()) {
                        cityDefTeamNum++;
                    } else if (team.getSoldierType() == eTonBingSoldierType.EXTRA_SOLDIER.getValue()) {
                        cityExtraDefTeamNum++;
                    }
                }
            }
        }


        ChannelTonBingProto.CTonBingBattlefieldMsg.Builder builder = ChannelTonBingProto.CTonBingBattlefieldMsg.newBuilder();
        builder.setPartId(part.getPartId());
        int defNo = part.getDefDeadCount();
        for (ChannelTonBingBattlefieldTeam team : new LinkedList<>(part.getDefList())) {
            builder.addDef(parseTonBingBattlefieldTeamMsg(city, team, ++defNo));
        }
        int atkNo = part.getAtkDeadCount();
        for (ChannelTonBingBattlefieldTeam team : new LinkedList<>(part.getAtkList())) {
            builder.addAtk(parseTonBingBattlefieldTeamMsg(city, team, ++atkNo));
        }

        int rankNum = 0;
        for (ChannelTonBingBattlefieldRank rank : new ArrayList<>(battlefield.getBattlefieldRankNewList())) {
            if (rankNum++ > 5) {
                break;
            }
            builder.addRank(parseTonBingBattlefieldRankMsg(rank));
        }

        builder.setBattleNo(city.getBattledNum() + 1);
        builder.setBattleTime(battlefield.getCreateTime() + config.getBattlePrepareTime());
        builder.setDrumTimes(battlefield.getDrumTimes());
        builder.addAllPartList(battlefield.getPartMap().keySet());
        builder.setCityId(city.getCityId());
        builder.setWinType(part.getWinType());
        builder.setDefNum(defNum);
        builder.setAtkNum(atkNum);
        builder.setCityDefTeamNum(cityDefTeamNum);
        builder.setCityExtraDefTeamNum(cityExtraDefTeamNum);
        return builder;
    }

    public ChannelTonBingProto.CTonBingBattlefieldRankMsg.Builder parseTonBingBattlefieldRankMsg(ChannelTonBingBattlefieldRank rank) {
        ChannelTonBingProto.CTonBingBattlefieldRankMsg.Builder builder = ChannelTonBingProto.CTonBingBattlefieldRankMsg.newBuilder();
        builder.setUser(parseTonBingUserMsg(rank.getUserId()));
        builder.setValue(rank.getZhanGong());
        builder.setKillNum(rank.getKillNum());
        return builder;
    }

    public ChannelTonBingProto.CTonBingBattlefieldTeamMsg.Builder parseTonBingBattlefieldTeamMsg(ChannelTonBingCity city, ChannelTonBingBattlefieldTeam battlefieldTeam, int no) {
        ChannelTonBingProto.CTonBingBattlefieldTeamMsg.Builder builder = ChannelTonBingProto.CTonBingBattlefieldTeamMsg.newBuilder();
        builder.setTeamNo(no);

        if (battlefieldTeam.getTeam() != null) {
            ChannelTonBingTeamArrive arrive = battlefieldTeam.getTeam();
            ChannelTonBingTeam team = getTeamByArrive(arrive);
            ChannelTonBingUserData userData = getUserData(arrive.getUserId());
            int patronsId = team.getPatronsList().isEmpty() ? 0 : team.getPatronsList().get(0);
            int skinId = userData.getPatronsMap().containsKey(patronsId) ? userData.getPatronsMap().get(patronsId).getSkin() : 0;

            builder.setUser(parseTonBingUserMsg(team.getUserId()));
            builder.setSoldierNum(Math.max(team.getSoldierNum(), 1)); // 战场内最少1个兵，不然客户端显示有问题
            builder.setPatronsId(patronsId);
            builder.setSkinId(skinId);
            builder.setSoldierMaxNum(team.getMaxSoldier());
        } else {
            // builder.setUser();
            builder.setSoldierNum(Math.max(battlefieldTeam.getSoldierNum(), 1)); // 战场内最少1个兵，不然客户端显示有问题
            builder.setPatronsId(0);
            builder.setSkinId(0);
            builder.setSoldierMaxNum(city.getTeamSoldierNum());
        }

        return builder;
    }

    public ChannelTonBingProto.CTonBingUserMsg.Builder parseTonBingUserMsg(long userId) {
        ChannelTonBingUserData userData = getUserData(userId);
        UserBaseInfo userBaseInfo = ChannelUserMgr.getUserBaseInfo(userId);
        String nickname = userBaseInfo == null ? "" : userBaseInfo.getNickName();

        ChannelTonBingProto.CTonBingUserMsg.Builder builder = ChannelTonBingProto.CTonBingUserMsg.newBuilder();
        builder.setUserId(userId);
        builder.setCampId(userData.getCampId());
        builder.setNickname(nickname);
        return builder;
    }

    public ChannelTonBingProto.CTonBingCampRankMsg.Builder parseTonBingCampRankMsg(ChannelTonBingRank rank) {
        Integer campId = Integer.valueOf(rank.getId());

        // 拿一下阵营第一的昵称
        UserBaseInfo userBaseInfo = getCampMaxZhanGongUserBaseInfo(campId);
        String nickname = userBaseInfo == null ? "" : userBaseInfo.getNickName();

        ChannelTonBingProto.CTonBingCampRankMsg.Builder builder = ChannelTonBingProto.CTonBingCampRankMsg.newBuilder();
        builder.setCampId(campId);
        builder.setValue(rank.getValue());
        builder.setMaxNickname(nickname);
        return builder;
    }

    public ChannelTonBingProto.CTonBingCampUnionRankMsg.Builder parseTonBingCampUnionRankMsg(ChannelTonBingRank rank,int warType) {
        String unionUid = rank.getId();
        ChannelTonBingUnionJoin channelTonBingUnionJoin = unionJoinMap.get(unionUid);
        UnionBaseInfo unionBaseInfo = channelTonBingUnionJoin == null ? new UnionBaseInfo() : channelTonBingUnionJoin.getUnionBaseInfo();
        ChannelTonBingProto.CTonBingCampUnionRankMsg.Builder builder = ChannelTonBingProto.CTonBingCampUnionRankMsg.newBuilder();
        builder.setUnionUid(unionUid);
        builder.setValue(rank.getValue());
        builder.setUnionInfo(UnionBasePb.parseUnionBaseTempMsg(unionBaseInfo));
        builder.setCampId(channelTonBingUnionJoin == null ? 0 : channelTonBingUnionJoin.getCampId());
        builder.setWarType(warType);
        return builder;
    }

    public ChannelTonBingProto.CTonBingUserRankMsg.Builder parseTonBingUserRankMsg(ChannelTonBingRank rank) {
        Long userId = Long.valueOf(rank.getId());
        UserBaseInfo userBaseInfo = ChannelUserMgr.getUserBaseInfo(userId);
        if (userBaseInfo == null) {
            userBaseInfo = new UserBaseInfo();
        }
        int campId = getUserData(userId).getCampId();

        ChannelTonBingProto.CTonBingUserRankMsg.Builder builder = ChannelTonBingProto.CTonBingUserRankMsg.newBuilder();
        builder.setUserId(userId);
        builder.setPlayerBaseData(PlayerBasePb.parsePlayerBaseTempMsg(userBaseInfo));
        builder.setValue(rank.getValue());
        builder.setCampId(campId);
        return builder;
    }

    public ChannelTonBingProto.CTonBingRedPacketMsg.Builder parseTonBingRedPacketMsg(ChannelTonBingRedPacket redPacket, boolean getReward) {
        UserBaseInfo userBaseInfo = ChannelUserMgr.getUserBaseInfo(redPacket.getUserId());
        if (userBaseInfo == null) {
            userBaseInfo = new UserBaseInfo();
        }

        ChannelTonBingProto.CTonBingRedPacketMsg.Builder tempMsg = ChannelTonBingProto.CTonBingRedPacketMsg.newBuilder();
        tempMsg.setUserId(redPacket.getUserId());
        tempMsg.setNickName(userBaseInfo.getNickName());
        tempMsg.setUnionName(userBaseInfo.getUnionName());
        tempMsg.setType(redPacket.getGoodId());
        tempMsg.setOrderNo("" + redPacket.getRedPacketNo());
        tempMsg.setIsGet(getReward ? 1 : 0);
        return tempMsg;
    }

    // ============================== 聊天 ==============================

    /**
     * 添加系统聊天通知
     *
     * @param sendTime 毫秒，进来再转秒
     */
    private void addSystemChat(eMessageType messageType, int id, String content, long sendTime) {
        addSystemChat(messageType, null, id, content, sendTime);
    }

    /**
     * 添加系统聊天通知
     *
     * @param sendTime 毫秒，进来再转秒
     */
    private void addSystemChat(eMessageType messageType, Long userId, int id, String content, long sendTime) {
        // 转换秒
        sendTime = sendTime / 1000;

        int range = 0;
        if (messageType == eMessageType.TonBingPublicBattleChat) {
            range = 1;
        } else if (messageType == eMessageType.TonBingCampHelpChat) {
            range = 2;
        } else if (messageType == eMessageType.TonBingBattlefieldWinChat) {
            range = 3;
        }

        if (range <= 0) {
            return;
        }

        WorldMessageProto.WorldMessageTempMsg.Builder builder = WorldMessageProto.WorldMessageTempMsg.newBuilder();
        builder.setType(messageType.getValue());
        builder.setSendTime(sendTime);
        builder.setContent(content);
        builder.setActivityId(config.getActivityInfo().getActivityId());
        if (userId != null) {
            UserBaseInfo userBaseInfo = ChannelUserMgr.getUserBaseInfo(userId);
            if (userBaseInfo != null) {
                builder.setUserId(userId);
                builder.setPlayerBaseData(PlayerBasePb.parsePlayerBaseTempMsg(userBaseInfo));
            }
        }
        chatLogic.chat(range, id, 0, builder.build(),channelTonBingData.getMatchId());
    }

    public void syncChat(int range, int id, WorldMessageProto.WorldMessageTempMsg chatMsg,int matchId) {
        long now = System.currentTimeMillis();
        List<Long> userIdList = new ArrayList<>();
        if (range == ChannelTonBingChatLogic.RANGE_ALL) {
            //所有人都接收的信息里面有活动聊天和系统公告，只有一个公告，现在变成多个公告了
            if(matchId != 0){
                userIdList = watchUserIdList;
                sendChannelPacketToAll(ClientProtocol.U_WORLD_MESSAGE_SYNC, chatMsg.toBuilder(), userIdList);
                return;
            }
            // 所有人都要接收，所以换个发包方式
            for(int channelId : channelIdList){
                for (long serverId : config.getActivityInfo().getServerIdList()) {
                    if(ChannelActivityUnionMgr.isServerIdInChannel(serverId,channelId)){
                        sendChannelPacket(Protocol.A_WORLD_MESSAGE_TRANSFER,chatMsg.toBuilder(),serverId,channelId);
                    }
                }
            }
            return;
        } else if (range == ChannelTonBingChatLogic.RANGE_CAMP) {
            userIdList = userDataMap.values().stream().filter(item -> item.getCampId() == id && isInActivity(item, now)).mapToLong(item -> item.getUserId()).boxed().collect(Collectors.toList());
        } else if (range == ChannelTonBingChatLogic.RANGE_BATTLEFIELD) {
            ChannelTonBingBattlefield battlefield = getChannelTonBingData().getCityMap().get(id).getBattlefield();
            if (battlefield != null) {
                for (Integer partId : battlefield.getPartMap().keySet()) {
                    List<Long> list = ChannelSceneMgr.getSceneUserList(config.getActivityInfo().getActivityId(),
                            eSceneType.TonBingBattlefield.getValue(),
                            channelTonBingData.getMatchId() + "-" + getBattlefieldSceneKey(id, partId),
                            23333)
                            .stream().mapToLong(item -> item.getUserId()).boxed().collect(Collectors.toList());
                    userIdList.addAll(list);
                }
            }
        }
        if (!userIdList.isEmpty()) {
            sendChannelPacketToAll(ClientProtocol.U_WORLD_MESSAGE_SYNC, chatMsg.toBuilder(), userIdList);
        }
    }

    // ============================== 其他 ==============================

    public void save() {
        if (channelTonBingData.isInsertOption()) {
            ChannelTonBingActivityBusiness.addChannelTonBingData(channelTonBingData);
        } else if (channelTonBingData.isUpdateOption()) {
            ChannelTonBingActivityBusiness.updateChannelTonBingData(channelTonBingData);
        }

        List<ChannelTonBingCityWarReport> tempNeedInsertCityReportList;
        synchronized (lock) {
            tempNeedInsertCityReportList = new ArrayList<>(needInsertCityReportList);
            needInsertCityReportList.clear();
        }
        if (tempNeedInsertCityReportList.size() > 0) {
            ChannelTonBingActivityBusiness.addCityReportList(tempNeedInsertCityReportList);
        }

        List<ChannelTonBingUserWarReport> tempNeedInsertUserReportList;
        synchronized (lock) {
            tempNeedInsertUserReportList = new ArrayList<>(needInsertUserReportList);
            needInsertUserReportList.clear();
        }
        if (tempNeedInsertUserReportList.size() > 0) {
            ChannelTonBingActivityBusiness.addUserReportList(tempNeedInsertUserReportList);
        }

        for (ChannelTonBingCampMatchData campData : campMatchDataMap.values()) {
            if (campData.isInsertOption()) {
                ChannelTonBingActivityBusiness.addChannelTonBingCampMatchData(campData);
            } else if (campData.isUpdateOption()) {
                ChannelTonBingActivityBusiness.updateChannelTonBingCampMatchData(campData);
            }
        }

    }

    /**
     * 玩家是否在界面内
     */
    public boolean isInActivity(ChannelTonBingUserData data, long now) {
        if (!isAble(data)) {
            return false; // 无资格的都进不了界面
        }
        if (now - data.getLastReqTime() < DateHelper.MINUTE_MILLIONS * 3) {
            return true; // 3分钟内有请求的都当他在线
        }
        return false;
    }

    /**
     * 设置请求时间
     */
    public void setLastReqTime(long userId) {
        if (userId <= 0) {
            return;
        }
        ChannelTonBingUserData userData = getUserData(userId);
        userData.setLastReqTime(System.currentTimeMillis());
    }

    public void printTask() {
        List<Long> userList = new ArrayList<>(watchUserIdList);
        for(Long userId : userList){
            if(userId == null){
                logger.error("watchUserIdList出现空的值");
                watchUserIdList.remove(null);
            }
        }
        Map<String, Integer> countMap = new ConcurrentHashMap<>();
        for (BaseChannelTonBingMatchTask task : taskQueue) {
            countMap.put(task.getType().name(), countMap.getOrDefault(task.getType().name(), 0) + 1);
        }
        logger.info("【统兵演武任务{}比赛】 - {}", channelTonBingData.getMatchId(), JSON.toJSONString(countMap));

        try {
            // 2023年4月23日11:37:30 热更 没战斗的城池让他继续战斗
            if (true) { // 2023-04-23 12:30:00前有效

                Set<Integer> taskSet = new HashSet<>();
                for (BaseChannelTonBingMatchTask task : taskQueue) {
                    if (task.getType() == eTonBingTaskType.Battle) {
                        ChannelTonBingBattleTask task2 = (ChannelTonBingBattleTask) task;
                        taskSet.add(Integer.valueOf(task2.getId()));
                    }
                }

                for (ChannelTonBingCity city : getChannelTonBingData().getCityMap().values()) {
                    if (city.getBattlefield() != null && !taskSet.contains(city.getCityId())) {
                        addTask(new ChannelTonBingBattleTask(this, calcNextBattleTime(city), city.getCityId()));
                        logger.error("activityId {} turn {} warType {} matchId {} cityId {} addBattleTask.",
                                config.getActivityInfo().getActivityId(),
                                channelTonBingData.getTurn(),
                                channelTonBingData.getWarType(),
                                channelTonBingData.getMatchId(),
                                city.getCityId()
                        );
                    }
                }

            }
        } catch (Exception e) {
            logger.error("error : ", e);
        }

    }

    public String getBattlefieldSceneKey(int cityId, int partId) {
        return cityId + "-" + partId;
    }

    public void syncNeedSyncTeam() {
        // 拿数据
        List<String> teamKeyList;
        synchronized (needSyncTeam) {
            teamKeyList = new ArrayList<>(needSyncTeam);
            needSyncTeam.clear();
        }
        // 没数据不同步
        if (teamKeyList.isEmpty()) {
            return;
        }
        // 拿在游戏内玩家
        long now = System.currentTimeMillis();
//        List<Long> userList = userDataMap.values().stream().filter(item -> isInActivity(item, now)).map(item -> item.getUserId()).collect(Collectors.toList());
        //玩家可以观战了，在这里面的都要同步
        List<Long> userList = new ArrayList<>(watchUserIdList);
        if (userList.isEmpty()) {
            return;
        }
        // 转化格式
        List<ChannelTonBingTeam> teamList = new ArrayList<>();
        for (String teamKey : teamKeyList) {
            teamList.add(getTeamByKey(teamKey));
        }
        // 同步
        ChannelTonBingProto.CTonBingSyncTeamRespMsg.Builder builder = ChannelTonBingProto.CTonBingSyncTeamRespMsg.newBuilder();
        for (ChannelTonBingTeam team : teamList) {
            builder.addTeam(parseTonBingTeamMsg(getUserData(team.getUserId()), team));
        }
        sendChannelPacketToAll(ClientProtocol.U_CTON_BING_SYNC_TEAM, builder, userList);
    }

    public void addNeedSyncTeam(ChannelTonBingTeam team) {
        String key = getTeamKey(team);
        synchronized (needSyncTeam) {
            needSyncTeam.add(key);
            // 20个就提前同步
            if (needSyncTeam.size() > 20) {
                syncNeedSyncTeam();
            }
        }
    }

    public void addNeedSyncCity(int cityId) {
        synchronized (needSyncCity) {
            needSyncCity.add(cityId);
            // 20个就提前同步
            if (needSyncCity.size() > 20) {
                syncNeedSyncCity();
            }
        }
    }

    public void syncNeedSyncCity() {
        // 拿数据
        List<Integer> cityIdList;
        synchronized (needSyncCity) {
            cityIdList = new ArrayList<>(needSyncCity);
            needSyncCity.clear();
        }
        // 没数据不同步
        if (cityIdList.isEmpty()) {
            return;
        }
        // 拿在游戏内玩家
        long now = System.currentTimeMillis();
        //在的都能看
//        List<Long> userList = userDataMap.values().stream().filter(item -> isInActivity(item, now)).map(item -> item.getUserId()).collect(Collectors.toList());
        List<Long> userList = new ArrayList<>(watchUserIdList);
        if (userList.isEmpty()) {
            return;
        }
        // 转化格式
        List<ChannelTonBingCity> cityList = new ArrayList<>();
        Map<Integer, ChannelTonBingCity> cityMap = getChannelTonBingData().getCityMap();
        for (int cityId : cityIdList) {
            cityList.add(cityMap.get(cityId));
        }
        // 同步
        ChannelTonBingProto.CTonBingSyncCityRespMsg.Builder builder = ChannelTonBingProto.CTonBingSyncCityRespMsg.newBuilder();
        for (ChannelTonBingCity city : cityList) {
            builder.addCity(parseTonBingCityMsg(city));
        }
        sendChannelPacketToAll(ClientProtocol.U_CTON_BING_SYNC_CITY, builder, userList);
    }

    public String getTeamKey(ChannelTonBingTeam team) {
        return team.getUserId() + "-" + team.getOccupation();
    }

    public String getTeamKey(ChannelTonBingTeamArrive arrive) {
        return arrive.getUserId() + "-" + arrive.getTeamId();
    }

    public ChannelTonBingTeam getTeamByKey(String key) {
        String[] split = key.split("-");
        ChannelTonBingUserData userData = getUserData(Long.valueOf(split[0]));
        return userData.getTeamMap().get(Integer.valueOf(split[1]));
    }

    public List<Long> getBattlefieldUserIdList(int cityId, int partId) {
        String sceneKey = getBattlefieldSceneKey(cityId, partId);
        List<UserScene> sceneUserList = ChannelSceneMgr.getSceneUserList(
                config.getActivityInfo().getActivityId(),
                eSceneType.TonBingBattlefield.getValue(),
                channelTonBingData.getMatchId() + "-" + sceneKey,
                23333);
        return sceneUserList.stream().mapToLong(UserScene::getUserId).boxed().collect(Collectors.toList());
    }

    /**
     * 领取榜单奖励
     */
    private int getRankReward(long userId, int conditionType) {
        ChannelTonBingUserData userData = getUserData(userId);

        // 资格
        if (!isAble(userData)) {
            return GameErrorCode.E_TON_BING_NO_ABLE;
        }

        // 拿职位参数
        ChannelTonBingUnionJoin join = unionJoinMap.get(userData.getUnionUid());
        ChannelTonBingUnionJoinMember member = join.getMemberMap().get(userId);
        int position = member.getPosition();

        // 拿阵营排名参数
        LinkedList<ChannelTonBingRank> campRankList = getCampScoreRankList();
        String myCampId = String.valueOf(userData.getCampId());
        int myCampRank = -1;
        int campRankNum = 0;
        for (ChannelTonBingRank rank : campRankList) {
            campRankNum++;
            if (Objects.equals(rank.getId(), myCampId)) {
                myCampRank = campRankNum;
                break;
            }
        }

        // 拿榜单
        String myId;
        LinkedList<ChannelTonBingRank> rankList;
        if (conditionType == eGamePlayerEventType.TonBingUserZhanGongRank.getValue()) {
            rankList = userRank.getRankList();
            myId = String.valueOf(userId);
        } else if (conditionType == eGamePlayerEventType.TonBingCampUnionZhanGongRank.getValue()) {
            ChannelTonBingRankListModel unionRank = campUnionRankMap.get(userData.getCampId());
            rankList = unionRank == null ? new LinkedList<>() : unionRank.getRankList();
            myId = userData.getUnionUid();
        } else if (conditionType == eGamePlayerEventType.TonBingCampScoreRank.getValue()) {
            rankList = campRankList;
            myId = String.valueOf(userData.getCampId());
        } else {
            return GameErrorCode.E_TON_BING_PARAM_ERR;
        }

        // 【拿榜单排名】
        int myRank = -1;
        int num = 0;
        for (ChannelTonBingRank rank : rankList) {
            num++;
            if (Objects.equals(rank.getId(), myId)) {
                myRank = num;
                break;
            }
        }

        ChannelTonBingProto.ChannelTonBingGetRankRewardRespMsg.Builder builder = ChannelTonBingProto.ChannelTonBingGetRankRewardRespMsg.newBuilder();
        builder.setRet(0);
        builder.setActivityId(config.getActivityInfo().getActivityId());
        builder.setConditionType(conditionType);
        builder.setPosition(position);
        builder.setRank(myRank);
        builder.setCampRank(myCampRank);
        sendChannelPacket(GameProtocol.S_CTON_BING_GET_RANK_REWARD_FROM_CHANNEL, builder, userData);
        return 0;
    }

    /**
     * 通知游戏服condition更新值
     */
    private void notifyConditionChange(long userId, eGamePlayerEventType type, long value) {
        ChannelTonBingUserData userData = getUserData(userId);

        if (type != eGamePlayerEventType.TonBingZhanGong) {
            return;
        }

        ChannelTonBingProto.ChannelTonBingNotifyConditionRespMsg.Builder builder = ChannelTonBingProto.ChannelTonBingNotifyConditionRespMsg.newBuilder();
        builder.setUserId(userId);
        builder.setConditionType(type.getValue());
        builder.setValue(value);
        sendChannelPacket(GameProtocol.S_CTON_BING_NOTIFY_CONDITION_FROM_CHANNEL, builder, userData);
    }

    public ChannelTonBingRankListModel getUserRank() {
        return userRank;
    }

    public Map<Integer, ChannelTonBingRankListModel> getCampUnionRankMap() {
        Map<Integer, ChannelTonBingRankListModel> campUnionMap = new ConcurrentHashMap<>();
        for (ChannelTonBingUnionJoin join : unionJoinMap.values()) {
            if (join.getCampId() != 0) {
                // 【阵营内商会榜】
                campUnionMap.putIfAbsent(join.getCampId(), new ChannelTonBingRankListModel());
                ChannelTonBingRank rank = getUnionZhanGongRankThisMatch(join);
                campUnionMap.get(join.getCampId()).rankChange(rank);
            }
        }
        return campUnionMap;
    }

    public LinkedList<ChannelTonBingRank> getAllUnionRankMap() {
        LinkedList<ChannelTonBingRank> rankList = new LinkedList<>();
        for(ChannelTonBingRankListModel model : campUnionRankMap.values()){
            rankList.addAll(model.getRankList());
        }
        rankList.sort(new ChannelTonBingRankSort());
        return rankList;
    }

    public Map<String, ChannelTonBingUnionJoin> getUnionJoinMap() {
        return unionJoinMap;
    }

    /**
     * 同步游戏内顶部消息
     *
     * @param type        1攻占成功，2防守成功
     * @param battlefield 可以为空
     */
    public void syncTopMsg(int campId, int cityId, int type, ChannelTonBingBattlefield battlefield, int defCampId) {

        // 拿在界面内玩家
        long now = System.currentTimeMillis();
        /*Set<Long> userIdSet = new HashSet<>(
                userDataMap.values().stream().filter(item -> isInActivity(item, now)).map(item -> item.getUserId()).collect(Collectors.toList())
        );*/

        // 2023年4月23日11:34:08 热更 避免并发报错

        ArrayList<Long> tmpList = new ArrayList<>(watchUserIdList);
        Set<Long> userIdSet = new HashSet<>();

        //只同步给有资格的玩家
        for (Long userId : tmpList) {
            ChannelTonBingUserData userData = getUserData(userId);
            if (userData == null) {
                continue;
            }
            userIdSet.add(userId);
        }

        // 战场兼容null
        if (battlefield != null) {
            // 有战功的单独同步
            for (ChannelTonBingBattlefieldRank rank : battlefield.getBattlefieldRankNewList()) {
                if (rank.getZhanGong() > 0 && userIdSet.contains(rank.getUserId())) {

                    ChannelTonBingProto.CTonBingSyncTopMsgRespMsg.Builder builder = ChannelTonBingProto.CTonBingSyncTopMsgRespMsg.newBuilder();
                    builder.setCampId(campId);
                    builder.setCityId(cityId);
                    builder.setType(type);
                    builder.setGetZhanGong(rank.getZhanGong());
                    builder.setDefCampId(defCampId);
                    ChannelTonBingUserData userData = getUserData(rank.getUserId());

                    sendChannelPacket(ClientProtocol.U_CTON_BING_SYNC_TOP_MSG, builder, userData);

                    // 记得移除单独同步玩家
                    userIdSet.remove(rank.getUserId());
                }
            }
        }

        if (!userIdSet.isEmpty()) {
            // 其余的批量同步
            ChannelTonBingProto.CTonBingSyncTopMsgRespMsg.Builder builder = ChannelTonBingProto.CTonBingSyncTopMsgRespMsg.newBuilder();
            builder.setCampId(campId);
            builder.setCityId(cityId);
            builder.setType(type);
            builder.setGetZhanGong(0);
            builder.setDefCampId(defCampId);
            sendChannelPacketToAll(ClientProtocol.U_CTON_BING_SYNC_TOP_MSG, builder, userIdSet);
        }
    }

    public void changeHorse(long userId, int horseId) {
        ChannelTonBingUserData userData = getUserData(userId);
        synchronized (lock) {

            if (horseId <= userData.getHorseId()) {
                logger.info("ton bing change horse fail! activityId {} userId {} horseId {} newHorseId {}.",
                        config.getActivityInfo().getActivityId(), userData.getUserId(), userData.getHorseId(), horseId);
                return;
            }

            // 换马
            userData.setHorseId(horseId);

            // 没在活动时间内
            long now = System.currentTimeMillis();
            if (now < config.getStage1TimeList().get(0).get(0) || now > config.getActivityInfo().getEndTime() * 1000L) {
                return;
            }

            // 同步
            for (ChannelTonBingTeam team : userData.getTeamMap().values()) {
                if (getTeamStatus(team) == eTonBingTeamStatus.Move) {
                    addNeedSyncTeam(team);
                }
            }
        }
    }

    /**
     * 邮件补发辎重
     */
    public void mailSendCityCollect() {
        int turn = channelTonBingData.getTurn();
        // 没到领奖期，不发
        if (System.currentTimeMillis() < config.getStage1TimeList().get(turn - 1).get(1) + DateHelper.HOUR_MILLIONS) {
            return;
        }

        synchronized (lock) {

            // 已经发过
            if (getChannelTonBingData().getAutoSendCollect() == 1) {
                return;
            }

            // 重新计算辎重
            for (int campId = 1; campId <= config.getCampNum(); campId++) {
                makeAllCityCollect(campId, config.getStage1TimeList().get(turn - 1).get(1) + DateHelper.HOUR_MILLIONS);
            }

            // 统计全部玩家数据
            Map<Long, Map<Long, Long>> serverUserCountMap = new ConcurrentHashMap<>();
            for (ChannelTonBingUserData userData : userDataMap.values()) {
                long userId = userData.getUserId();
                // 没资格
                if (!isAble(userData)) {
                    continue;
                }
                // 拿数量
                long userAllValue = 0L;
                ChannelTonBingCampMatchData campData = campMatchDataMap.get(userData.getCampId());
                if (campData != null) {
                    Map<Long, Map<Integer, Long>> userCityValueMap = campData.getCollectionMap();
                    Map<Integer, Long> cityValueMap = userCityValueMap.getOrDefault(userData.getUserId(), new ConcurrentHashMap<>());
                    for (Long value : cityValueMap.values()) {
                        userAllValue += value;
                    }
                    userCityValueMap.put(userId, new ConcurrentHashMap<>());
                }
                // 统计值
                if (userAllValue > 0) {
                    UserBaseInfo userBaseInfo = ChannelUserMgr.getUserBaseInfo(userId);
                    if (userBaseInfo == null) {
                        logger.info("mailSendCityCollect can not find userInfo. activityId {} userId {} count {}.",
                                config.getActivityInfo().getActivityId(), userId, userAllValue);
                    } else {
                        serverUserCountMap.putIfAbsent(userBaseInfo.getServerId(), new ConcurrentHashMap<>());
                        serverUserCountMap.get(userBaseInfo.getServerId()).put(userId, userAllValue);
                    }
                }
            }

            // 日志
            logger.info("ton bing activityId {} , mailSendCityCollect : {}",
                    config.getActivityInfo().getActivityId(), JSON.toJSONString(serverUserCountMap));

            // 改状态
            getChannelTonBingData().setAutoSendCollect(1);
            for (ChannelTonBingCampMatchData campData : campMatchDataMap.values()) {
                campData.setUpdateOption();
            }

            // 按区服发奖
            for (Map.Entry<Long, Map<Long, Long>> entry : serverUserCountMap.entrySet()) {
                Long serverId = entry.getKey();
                ChannelTonBingProto.ChannelTonBingMailSendCityCollectReqMsg.Builder builder = ChannelTonBingProto.ChannelTonBingMailSendCityCollectReqMsg.newBuilder();
                for (Map.Entry<Long, Long> longEntry : entry.getValue().entrySet()) {
                    long userId = longEntry.getKey();
                    Long count = longEntry.getValue();
                    ChannelTonBingProto.ChannelTonBingCityCollectMsg.Builder tmp = ChannelTonBingProto.ChannelTonBingCityCollectMsg.newBuilder();
                    tmp.setUserId(userId);
                    tmp.setCount(count);
                    builder.addList(tmp);
                }
                for(int channelId : channelIdList){
                    if(ChannelActivityUnionMgr.isServerIdInChannel(serverId,channelId)){
                        sendChannelPacket(GameProtocol.S_CTON_BING_AUTO_SEND_CITY_COLLECT, builder,serverId,channelId);
                    }
                }
            }

        }
    }

    /**
     * 获取阵营最高战功的玩家
     */
    private UserBaseInfo getCampMaxZhanGongUserBaseInfo(int campId) {
        long userId = getCampMaxZhanGongUserId(campId);
        return userId == 0 ? null : ChannelUserMgr.getUserBaseInfo(userId);
    }

    /**
     * 获取阵营最高战功的玩家Id
     */
    public long getCampMaxZhanGongUserId(int campId) {
        long userId = 0;
        ChannelTonBingRankListModel rankListModel = campUserRankMap.get(campId);
        if (rankListModel != null) {
            LinkedList<ChannelTonBingRank> rankList = rankListModel.getRankList();
            rankList.sort(new ChannelTonBingRankSort());
            if (!rankList.isEmpty()) {
                ChannelTonBingRank first = rankList.getFirst();
                userId = Long.parseLong(first.getId());
            }
        }
        return userId;
    }

    private ChannelTonBingProto.ChannelTonBingCityWinerAppointRespMsg.Builder parsePos(ChannelTonBingProto.ChannelTonBingCityWinerAppointRespMsg.Builder msg,Map<Integer, Integer> positionMap){
        Map<Integer, Integer> posMap = new ConcurrentHashMap(positionMap);
        for(int type : posMap.keySet()){
            ChannelTonBingProto.CTonBingPositionMsg.Builder posMsg = ChannelTonBingProto.CTonBingPositionMsg.newBuilder();
            posMsg.setType(type);
            posMsg.setPositionId(posMap.get(type));
            msg.addPosition(posMsg);
        }
        return msg;
    }


    // ========= 预留方法 =========

    public String hotCode(String param) {
        return "";
    }

}
